- Version changed in scst_user_spec.txt
[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
28 #include <sys/types.h>
29 #include <sys/user.h>
30 #include <sys/poll.h>
31 #include <sys/ioctl.h>
32
33 #include <pthread.h>
34
35 char *app_name;
36 #define LOG_PREFIX      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_DEBUG | TRACE_ORDER | \
50         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_PID | \
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_PR("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_PR("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_PR("Virtual device \"%s\", path \"%s\", size %LdMb, "
292                 "block size %d, nblocks %Ld, options:", dev.name, dev.file_name,
293                 dev.file_size/1024/1024, dev.block_size, dev.nblocks);
294         if (dev.rd_only_flag)
295                 PRINT_INFO("    %s", "READ ONLY");
296         if (dev.wt_flag)
297                 PRINT_INFO("    %s", "WRITE THROUGH");
298         if (dev.nv_cache)
299                 PRINT_INFO("    %s", "NV_CACHE");
300         if (dev.o_direct_flag)
301                 PRINT_INFO("    %s", "O_DIRECT");
302         if (dev.nullio)
303                 PRINT_INFO("    %s", "NULLIO");
304         if (dev.non_blocking)
305                 PRINT_INFO("    %s", "NON-BLOCKING");
306
307         switch(parse_type) {
308         case SCST_USER_PARSE_STANDARD:
309                 PRINT_INFO("    %s", "Standard parse");
310                 break;
311         case SCST_USER_PARSE_CALL:
312                 PRINT_INFO("    %s", "Call parse");
313                 break;
314         case SCST_USER_PARSE_EXCEPTION:
315                 PRINT_INFO("    %s", "Exception parse");
316                 break;
317         default:
318                 sBUG();
319         }
320
321         switch(on_free_cmd_type) {
322         case SCST_USER_ON_FREE_CMD_IGNORE:
323                 PRINT_INFO("    %s", "Ignore on_free_cmd");
324                 break;
325         case SCST_USER_ON_FREE_CMD_CALL:
326                 PRINT_INFO("    %s", "Call on_free_cmd");
327                 break;
328         default:
329                 sBUG();
330         }
331
332         switch(memory_reuse_type) {
333         case SCST_USER_MEM_REUSE_ALL:
334                 PRINT_INFO("    %s", "Full memory reuse enabled");
335                 break;
336         case SCST_USER_MEM_REUSE_READ:
337                 PRINT_INFO("    %s", "READ memory reuse enabled");
338                 break;
339         case SCST_USER_MEM_REUSE_WRITE:
340                 PRINT_INFO("    %s", "WRITE memory reuse enabled");
341                 break;
342         case SCST_USER_MEM_NO_REUSE:
343                 PRINT_INFO("    %s", "Memory reuse disabled");
344                 break;
345         default:
346                 sBUG();
347         }
348
349         if (!dev.o_direct_flag && (memory_reuse_type == SCST_USER_MEM_NO_REUSE)) {
350                 PRINT_INFO("    %s", "Using unaligned buffers");
351                 dev.alloc_fn = malloc;
352         }
353
354         if (dev.prio_thr) {
355                 PRINT_INFO("    %s", "Using separate prio thread");
356         }
357
358 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
359         if (dev.debug_tm_ignore) {
360                 PRINT_INFO("    %s", "DEBUG_TM_IGNORE");
361         }
362 #endif
363
364 #ifdef DEBUG
365         PRINT_INFO("trace_flag %lx", trace_flag);
366 #endif
367
368         dev.scst_usr_fd = open(DEV_USER_PATH DEV_USER_NAME, O_RDWR | 
369                 (dev.non_blocking ? O_NONBLOCK : 0));
370         if (dev.scst_usr_fd < 0) {
371                 res = dev.scst_usr_fd;
372                 PRINT_ERROR_PR("Unable to open SCST device %s (%s)",
373                         DEV_USER_PATH DEV_USER_NAME, strerror(res));
374                 goto out_done;
375         }
376
377         memset(&desc, 0, sizeof(desc));
378         desc.version = DEV_USER_VERSION;
379         strncpy(desc.name, dev.name, sizeof(desc.name)-1);
380         desc.name[sizeof(desc.name)-1] = '\0';
381         desc.type = dev.type;
382         desc.block_size = dev.block_size;
383
384         desc.opt.parse_type = parse_type;
385         desc.opt.on_free_cmd_type = on_free_cmd_type;
386         desc.opt.memory_reuse_type = memory_reuse_type;
387         if (dev.prio_thr)
388                 desc.opt.prio_queue_type = SCST_USER_PRIO_QUEUE_SEPARATE;
389         else
390                 desc.opt.prio_queue_type = SCST_USER_PRIO_QUEUE_SINGLE;
391
392         res = ioctl(dev.scst_usr_fd, SCST_USER_REGISTER_DEVICE, &desc);
393         if (res != 0) {
394                 res = errno;
395                 PRINT_ERROR_PR("Unable to register device: %s", strerror(res));
396                 goto out_close;
397         }
398
399 #if 1
400         {
401                 /* Not needed, added here only as a test */
402                 struct scst_user_opt opt;
403
404                 res = ioctl(dev.scst_usr_fd, SCST_USER_GET_OPTIONS, &opt);
405                 if (res != 0) {
406                         res = errno;
407                         PRINT_ERROR_PR("Unable to get options: %s", strerror(res));
408                         goto out_close;
409                 }
410
411                 opt.parse_type = parse_type;
412                 opt.on_free_cmd_type = on_free_cmd_type;
413                 opt.memory_reuse_type = memory_reuse_type;
414                 if (dev.prio_thr)
415                         opt.prio_queue_type = SCST_USER_PRIO_QUEUE_SEPARATE;
416                 else
417                         opt.prio_queue_type = SCST_USER_PRIO_QUEUE_SINGLE;
418
419                 res = ioctl(dev.scst_usr_fd, SCST_USER_SET_OPTIONS, &opt);
420                 if (res != 0) {
421                         res = errno;
422                         PRINT_ERROR_PR("Unable to get options: %s", strerror(res));
423                         goto out_close;
424                 }
425         }
426 #endif
427
428         res = pthread_mutex_init(&dev.dev_mutex, NULL);
429         if (res != 0) {
430                 res = errno;
431                 PRINT_ERROR_PR("pthread_mutex_init() failed: %s", strerror(res));
432                 goto out_close;
433         }
434
435         {
436                 pthread_t thread[threads];
437                 pthread_t prio;
438                 int i, j, rc;
439                 void *rc1;
440                 for(i = 0; i < threads; i++) {
441                         rc = pthread_create(&thread[i], NULL, main_loop, &dev);
442                         if (rc != 0) {
443                                 res = errno;
444                                 PRINT_ERROR_PR("pthread_create() failed: %s",
445                                         strerror(res));
446                                 break;
447                         }
448                 }
449
450                 if (dev.prio_thr) {
451                         rc = pthread_create(&prio, NULL, prio_loop, &dev);
452                         if (rc != 0) {
453                                 res = errno;
454                                 PRINT_ERROR_PR("Prio pthread_create() failed: %s",
455                                         strerror(res));
456                                 dev.prio_thr = 0;
457                         }
458                 }
459
460                 j = i;
461                 for(i = 0; i < j; i++) {
462                         rc = pthread_join(thread[i], &rc1);
463                         if (rc != 0) {
464                                 res = errno;
465                                 PRINT_ERROR_PR("pthread_join() failed: %s",
466                                         strerror(res));
467                         } else if (rc1 != NULL) {
468                                 res = (int)rc1;
469                                 PRINT_INFO("Thread %d exited, res %x", i, res);
470                         } else
471                                 PRINT_INFO("Thread %d exited", i);
472                 }
473                 if (dev.prio_thr) {
474                         rc = pthread_join(prio, &rc1);
475                         if (rc != 0) {
476                                 res = errno;
477                                 PRINT_ERROR_PR("Prio pthread_join() failed: %s",
478                                         strerror(res));
479                         } else if (rc1 != NULL) {
480                                 res = (int)rc1;
481                                 PRINT_INFO("Prio thread %d exited, res %x", i, res);
482                         } else
483                                 PRINT_INFO("Prio thread %d exited", i);
484                 }
485         }
486
487         pthread_mutex_destroy(&dev.dev_mutex);
488
489 out_close:
490         close(dev.scst_usr_fd);
491
492 out_done:
493         debug_done();
494
495 out:
496         return res;
497
498 out_usage:
499         usage();
500         goto out_done;
501 }