- scst_user docs updated
[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", strerror(res));
175                 goto out;
176         }
177
178         TRACE_DBG("%s", "Flushing cache done.");
179
180         res = alarm(flush_interval);
181         if (res != 0) {
182                 res = errno;
183                 PRINT_ERROR("alarm() failed: %s", strerror(res));
184                 goto out;
185         }
186
187 out:
188         TRACE_EXIT();
189         return;
190 }
191
192 void sigusr1_handler(int signo)
193 {
194         int res;
195
196         TRACE_ENTRY();
197
198         TRACE_MGMT_DBG("%s", "Capacity data changed...");
199
200         res = ioctl(dev.scst_usr_fd, SCST_USER_DEVICE_CAPACITY_CHANGED, NULL);
201         if (res != 0) {
202                 res = errno;
203                 PRINT_ERROR("Capacity data changed failed: %s", strerror(res));
204                 goto out;
205         }
206
207         TRACE_DBG("%s", "Capacity data changed done.");
208
209 out:
210         TRACE_EXIT();
211         return;
212 }
213
214 int main(int argc, char **argv)
215 {
216         int res = 0;
217         int ch, longindex;
218         int fd;
219         int parse_type = SCST_USER_PARSE_STANDARD;
220         int on_free_cmd_type = SCST_USER_ON_FREE_CMD_IGNORE;
221         int on_free_cmd_type_set = 0;
222         int memory_reuse_type = SCST_USER_MEM_REUSE_ALL;
223         int threads = THREADS;
224         struct scst_user_dev_desc desc;
225         int unreg_before_close = 0;
226
227         setlinebuf(stdout);
228
229         res = debug_init();
230         if (res != 0)
231                 goto out;
232
233         app_name = argv[0];
234
235         memset(&dev, 0, sizeof(dev));
236         dev.block_size = (1 << DEF_BLOCK_SHIFT);
237         dev.block_shift = DEF_BLOCK_SHIFT;
238         dev.type = TYPE_DISK;
239         dev.alloc_fn = align_alloc;
240
241         while ((ch = getopt_long(argc, argv, "+b:e:trongluF:I:cp:f:m:d:vh", long_options,
242                                 &longindex)) >= 0) {
243                 switch (ch) {
244                 case 'b':
245                         dev.block_size = atoi(optarg);
246                         PRINT_INFO("block_size %x (%s)", dev.block_size, optarg);
247                         dev.block_shift = scst_calc_block_shift(dev.block_size);
248                         if (dev.block_shift < 9) {
249                                 res = -EINVAL;
250                                 goto out_usage;
251                         }
252                         break;
253                 case 'e':
254                         threads = strtol(optarg, (char **)NULL, 0);
255                         break;
256                 case 't':
257                         dev.wt_flag = 1;
258                         break;
259 #if defined(DEBUG) || defined(TRACING)
260                 case 'd':
261                         trace_flag = strtol(optarg, (char **)NULL, 0);
262                         break;
263 #endif
264                 case 'r':
265                         dev.rd_only_flag = 1;
266                         break;
267                 case 'o':
268                         dev.o_direct_flag = 1;
269                         dev.alloc_fn = align_alloc;
270                         break;
271                 case 'n':
272                         dev.nullio = 1;
273                         break;
274                 case 'c':
275                         dev.nv_cache = 1;
276                         break;
277                 case 'p':
278                         if (strncmp(optarg, "std", 3) == 0)
279                                 parse_type = SCST_USER_PARSE_STANDARD;
280                         else if (strncmp(optarg, "call", 3) == 0)
281                                 parse_type = SCST_USER_PARSE_CALL;
282                         else if (strncmp(optarg, "excpt", 5) == 0)
283                                 parse_type = SCST_USER_PARSE_EXCEPTION;
284                         else
285                                 goto out_usage;
286                         break;
287                 case 'f':
288                         on_free_cmd_type_set = 1;
289                         if (strncmp(optarg, "ignore", 6) == 0)
290                                 on_free_cmd_type = SCST_USER_ON_FREE_CMD_IGNORE;
291                         else if (strncmp(optarg, "call", 3) == 0)
292                                 on_free_cmd_type = SCST_USER_ON_FREE_CMD_CALL;
293                         else
294                                 goto out_usage;
295                         break;
296                 case 'm':
297                         if (strncmp(optarg, "all", 3) == 0)
298                                 memory_reuse_type = SCST_USER_MEM_REUSE_ALL;
299                         else if (strncmp(optarg, "read", 4) == 0)
300                                 memory_reuse_type = SCST_USER_MEM_REUSE_READ;
301                         else if (strncmp(optarg, "write", 5) == 0)
302                                 memory_reuse_type = SCST_USER_MEM_REUSE_WRITE;
303                         else if (strncmp(optarg, "none", 4) == 0)
304                                 memory_reuse_type = SCST_USER_MEM_NO_REUSE;
305                         else
306                                 goto out_usage;
307                         break;
308                 case 'l':
309                         dev.non_blocking = 1;
310                         break;
311                 case 'I':
312                         vdisk_ID = strtol(optarg, (char **)NULL, 0);
313                         break;
314                 case 'F':
315                         flush_interval = strtol(optarg, (char **)NULL, 0);
316                         if (flush_interval < 0) {
317                                 PRINT_ERROR("Wrong flush interval %d",
318                                         flush_interval);
319                                 flush_interval = 0;
320                         }
321                         break;
322                 case 'u':
323                         unreg_before_close = 1;
324                         break;
325 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
326                 case 'g':
327                         dev.debug_tm_ignore = 1;
328                         break;
329 #endif
330                 case 'v':
331                         printf("%s version %s\n", app_name, VERSION_STR);
332                         goto out_done;
333                 default:
334                         goto out_usage;
335                 }
336         }
337
338         if (optind != (argc-2))
339                 goto out_usage;
340
341         if (!on_free_cmd_type_set &&
342             (memory_reuse_type != SCST_USER_MEM_REUSE_ALL))
343                 on_free_cmd_type = SCST_USER_ON_FREE_CMD_CALL;
344
345         dev.name = argv[optind];
346         dev.file_name = argv[optind+1];
347
348         TRACE_DBG("Opening file %s", dev.file_name);
349         fd = open(dev.file_name, O_RDONLY|O_LARGEFILE);
350         if (fd < 0) {
351                 res = -errno;
352                 PRINT_ERROR("Unable to open file %s (%s)", dev.file_name,
353                         strerror(-res));
354                 goto out_done;
355         }
356
357         dev.file_size = lseek64(fd, 0, SEEK_END);
358         dev.nblocks = dev.file_size >> dev.block_shift;
359
360         close(fd);
361
362         PRINT_INFO("Virtual device \"%s\", path \"%s\", size %"PRId64"Mb, "
363                 "block size %d, nblocks %"PRId64", options:", dev.name,
364                 dev.file_name, (uint64_t)dev.file_size/1024/1024,
365                 dev.block_size, (uint64_t)dev.nblocks);
366         if (dev.rd_only_flag)
367                 PRINT_INFO("    %s", "READ ONLY");
368         if (dev.wt_flag)
369                 PRINT_INFO("    %s", "WRITE THROUGH");
370         if (dev.nv_cache)
371                 PRINT_INFO("    %s", "NV_CACHE");
372         if (dev.o_direct_flag)
373                 PRINT_INFO("    %s", "O_DIRECT");
374         if (dev.nullio)
375                 PRINT_INFO("    %s", "NULLIO");
376         if (dev.non_blocking)
377                 PRINT_INFO("    %s", "NON-BLOCKING");
378
379         switch(parse_type) {
380         case SCST_USER_PARSE_STANDARD:
381                 PRINT_INFO("    %s", "Standard parse");
382                 break;
383         case SCST_USER_PARSE_CALL:
384                 PRINT_INFO("    %s", "Call parse");
385                 break;
386         case SCST_USER_PARSE_EXCEPTION:
387                 PRINT_INFO("    %s", "Exception parse");
388                 break;
389         default:
390                 sBUG();
391         }
392
393         switch(on_free_cmd_type) {
394         case SCST_USER_ON_FREE_CMD_IGNORE:
395                 PRINT_INFO("    %s", "Ignore on_free_cmd");
396                 break;
397         case SCST_USER_ON_FREE_CMD_CALL:
398                 PRINT_INFO("    %s", "Call on_free_cmd");
399                 break;
400         default:
401                 sBUG();
402         }
403
404         switch(memory_reuse_type) {
405         case SCST_USER_MEM_REUSE_ALL:
406                 PRINT_INFO("    %s", "Full memory reuse enabled");
407                 break;
408         case SCST_USER_MEM_REUSE_READ:
409                 PRINT_INFO("    %s", "READ memory reuse enabled");
410                 break;
411         case SCST_USER_MEM_REUSE_WRITE:
412                 PRINT_INFO("    %s", "WRITE memory reuse enabled");
413                 break;
414         case SCST_USER_MEM_NO_REUSE:
415                 PRINT_INFO("    %s", "Memory reuse disabled");
416                 break;
417         default:
418                 sBUG();
419         }
420
421         if (!dev.o_direct_flag && (memory_reuse_type == SCST_USER_MEM_NO_REUSE)) {
422                 PRINT_INFO("    %s", "Using unaligned buffers");
423                 dev.alloc_fn = malloc;
424         }
425
426 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
427         if (dev.debug_tm_ignore) {
428                 PRINT_INFO("    %s", "DEBUG_TM_IGNORE");
429         }
430 #endif
431
432 #ifdef DEBUG
433         PRINT_INFO("trace_flag %lx", trace_flag);
434 #endif
435
436         snprintf(dev.usn, sizeof(dev.usn), "%llx", gen_dev_id_num(&dev));
437         TRACE_DBG("usn %s", dev.usn);
438
439         dev.scst_usr_fd = open(DEV_USER_PATH DEV_USER_NAME, O_RDWR |
440                 (dev.non_blocking ? O_NONBLOCK : 0));
441         if (dev.scst_usr_fd < 0) {
442                 res = -errno;
443                 PRINT_ERROR("Unable to open SCST device %s (%s)",
444                         DEV_USER_PATH DEV_USER_NAME, strerror(-res));
445                 goto out_done;
446         }
447
448         memset(&desc, 0, sizeof(desc));
449         desc.version_str = (unsigned long)DEV_USER_VERSION;
450         strncpy(desc.name, dev.name, sizeof(desc.name)-1);
451         desc.name[sizeof(desc.name)-1] = '\0';
452         desc.type = dev.type;
453         desc.block_size = dev.block_size;
454
455         desc.opt.parse_type = parse_type;
456         desc.opt.on_free_cmd_type = on_free_cmd_type;
457         desc.opt.memory_reuse_type = memory_reuse_type;
458
459         desc.opt.tst = SCST_CONTR_MODE_SEP_TASK_SETS;
460         desc.opt.queue_alg = SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER;
461
462         res = ioctl(dev.scst_usr_fd, SCST_USER_REGISTER_DEVICE, &desc);
463         if (res != 0) {
464                 res = errno;
465                 PRINT_ERROR("Unable to register device: %s", strerror(res));
466                 goto out_close;
467         }
468
469 #if 1
470         {
471                 /* Not needed, added here only as a test */
472                 struct scst_user_opt opt;
473
474                 res = ioctl(dev.scst_usr_fd, SCST_USER_GET_OPTIONS, &opt);
475                 if (res != 0) {
476                         res = errno;
477                         PRINT_ERROR("Unable to get options: %s", strerror(res));
478                         goto out_unreg;
479                 }
480
481                 opt.parse_type = parse_type;
482                 opt.on_free_cmd_type = on_free_cmd_type;
483                 opt.memory_reuse_type = memory_reuse_type;
484
485                 res = ioctl(dev.scst_usr_fd, SCST_USER_SET_OPTIONS, &opt);
486                 if (res != 0) {
487                         res = errno;
488                         PRINT_ERROR("Unable to set options: %s", strerror(res));
489                         goto out_unreg;
490                 }
491         }
492 #endif
493
494         res = pthread_mutex_init(&dev.dev_mutex, NULL);
495         if (res != 0) {
496                 res = errno;
497                 PRINT_ERROR("pthread_mutex_init() failed: %s", strerror(res));
498                 goto out_unreg;
499         }
500
501         {
502                 pthread_t thread[threads];
503                 int i, j, rc;
504                 void *rc1;
505                 struct sigaction act;
506
507                 memset(&act, 0, sizeof(act));
508                 act.sa_handler = sigusr1_handler;
509                 act.sa_flags = SA_RESTART;
510                 sigemptyset(&act.sa_mask);
511                 res = sigaction(SIGUSR1, &act, NULL);
512                 if (res != 0) {
513                         res = errno;
514                         PRINT_ERROR("sigaction() failed: %s",
515                                 strerror(res));
516                         /* don't do anything */
517                 }       
518
519                 for(i = 0; i < threads; i++) {
520                         rc = pthread_create(&thread[i], NULL, main_loop, &dev);
521                         if (rc != 0) {
522                                 res = errno;
523                                 PRINT_ERROR("pthread_create() failed: %s",
524                                         strerror(res));
525                                 break;
526                         }
527                 }
528
529                 if (flush_interval != 0) {
530                         memset(&act, 0, sizeof(act));
531                         act.sa_handler = sigalrm_handler;
532                         act.sa_flags = SA_RESTART;
533                         sigemptyset(&act.sa_mask);
534                         res = sigaction(SIGALRM, &act, NULL);
535                         if (res != 0) {
536                                 res = errno;
537                                 PRINT_ERROR("sigaction() failed: %s",
538                                         strerror(res));
539                                 goto join;
540                         }       
541
542                         res = alarm(flush_interval);
543                         if (res != 0) {
544                                 res = errno;
545                                 PRINT_ERROR("alarm() failed: %s",
546                                         strerror(res));
547                                 goto join;
548                         }
549                 }
550
551 join:
552                 j = i;
553                 for(i = 0; i < j; i++) {
554                         rc = pthread_join(thread[i], &rc1);
555                         if (rc != 0) {
556                                 res = errno;
557                                 PRINT_ERROR("pthread_join() failed: %s",
558                                         strerror(res));
559                         } else if (rc1 != NULL) {
560                                 res = (long)rc1;
561                                 PRINT_INFO("Thread %d exited, res %lx", i,
562                                         (long)rc1);
563                         } else
564                                 PRINT_INFO("Thread %d exited", i);
565                 }
566         }
567
568         pthread_mutex_destroy(&dev.dev_mutex);
569
570         alarm(0);
571
572 out_unreg:
573         if (unreg_before_close) {
574                 res = ioctl(dev.scst_usr_fd, SCST_USER_UNREGISTER_DEVICE, NULL);
575                 if (res != 0) {
576                         res = errno;
577                         PRINT_ERROR("Unable to unregister device: %s",
578                                 strerror(res));
579                         /* go through */
580                 }
581         }
582
583 out_close:
584         close(dev.scst_usr_fd);
585
586 out_done:
587         debug_done();
588
589 out:
590         return res;
591
592 out_usage:
593         usage();
594         goto out_done;
595 }