The major TM processing cleanup in scst_user module which was possible after the...
[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         {"non_blocking", no_argument, 0, 'l'},
84 #if defined(DEBUG) || defined(TRACING)
85         {"debug", required_argument, 0, 'd'},
86 #endif
87 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
88         {"debug_tm_ignore", no_argument, 0, 'g'},
89 #endif
90         {"version", no_argument, 0, 'v'},
91         {"help", no_argument, 0, 'h'},
92         {0, 0, 0, 0},
93 };
94
95 static void usage(void)
96 {
97         printf("Usage: %s [OPTION] name path\n", app_name);
98         printf("\nFILEIO disk target emulator for SCST\n");
99         printf("  -b, --block=size      Block size, must be power of 2 and >=512\n");
100         printf("  -e, --threads=count   Number of threads, %d by default\n", THREADS);
101         printf("  -t, --write_through   Write through mode\n");
102         printf("  -r, --read_only       Read only\n");
103         printf("  -o, --direct          O_DIRECT mode\n");
104         printf("  -n, --nullio          NULLIO mode\n");
105         printf("  -c, --nv_cache        NV_CACHE mode\n");
106         printf("  -p, --parse=type      Parse type, one of \"std\" "
107                 "(default), \"call\" or \"excpt\"\n");
108         printf("  -f, --on_free=type    On free call type, one of \"ignore\" "
109                 "(default) or \"call\"\n");
110         printf("  -m, --mem_reuse=type  Memory reuse type, one of \"all\" "
111                 "(default), \"read\", \"write\" or \"none\"\n");
112         printf("  -l, --non_blocking    Use non-blocking operations\n");
113 #if defined(DEBUG) || defined(TRACING)
114         printf("  -d, --debug=level     Debug tracing level\n");
115 #endif
116 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
117         printf("  -g, --debug_tm_ignore Turn on DEBUG_TM_IGNORE\n");
118 #endif
119         return;
120 }
121
122 static int scst_calc_block_shift(int sector_size)
123 {
124         int block_shift = 0;
125         int t = sector_size;
126
127         if (sector_size == 0)
128                 goto out;
129
130         t = sector_size;
131         while(1) {
132                 if ((t & 1) != 0)
133                         break;
134                 t >>= 1;
135                 block_shift++;
136         }
137         if (block_shift < 9) {
138                 PRINT_ERROR("Wrong sector size %d", sector_size);
139                 block_shift = -1;
140         }
141
142 out:
143         TRACE_EXIT_RES(block_shift);
144         return block_shift;
145 }
146
147 static void *align_alloc(size_t size)
148 {
149         return memalign(PAGE_SIZE, size);
150 }
151
152 int main(int argc, char **argv)
153 {
154         int res = 0;
155         int ch, longindex;
156         int fd;
157         int parse_type = SCST_USER_PARSE_STANDARD;
158         int on_free_cmd_type = SCST_USER_ON_FREE_CMD_IGNORE;
159         int on_free_cmd_type_set = 0;
160         int memory_reuse_type = SCST_USER_MEM_REUSE_ALL;
161         int threads = THREADS;
162         struct scst_user_dev_desc desc;
163         struct vdisk_dev dev;
164
165         setlinebuf(stdout);
166
167         res = debug_init();
168         if (res != 0)
169                 goto out;
170
171         app_name = argv[0];
172
173         memset(&dev, 0, sizeof(dev));
174         dev.block_size = (1 << DEF_BLOCK_SHIFT);
175         dev.block_shift = DEF_BLOCK_SHIFT;
176         dev.type = TYPE_DISK;
177         dev.alloc_fn = align_alloc;
178
179         while ((ch = getopt_long(argc, argv, "+b:e:tronglcp:f:m:d:vh", long_options,
180                                 &longindex)) >= 0) {
181                 switch (ch) {
182                 case 'b':
183                         dev.block_size = atoi(optarg);
184                         PRINT_INFO("block_size %x (%s)", dev.block_size, optarg);
185                         dev.block_shift = scst_calc_block_shift(dev.block_size);
186                         if (dev.block_shift < 9) {
187                                 res = -EINVAL;
188                                 goto out_usage;
189                         }
190                         break;
191                 case 'e':
192                         threads = strtol(optarg, (char **)NULL, 0);
193                         break;
194                 case 't':
195                         dev.wt_flag = 1;
196                         break;
197 #if defined(DEBUG) || defined(TRACING)
198                 case 'd':
199                         trace_flag = strtol(optarg, (char **)NULL, 0);
200                         break;
201 #endif
202                 case 'r':
203                         dev.rd_only_flag = 1;
204                         break;
205                 case 'o':
206                         dev.o_direct_flag = 1;
207                         dev.alloc_fn = align_alloc;
208                         break;
209                 case 'n':
210                         dev.nullio = 1;
211                         break;
212                 case 'c':
213                         dev.nv_cache = 1;
214                         break;
215                 case 'p':
216                         if (strncmp(optarg, "std", 3) == 0)
217                                 parse_type = SCST_USER_PARSE_STANDARD;
218                         else if (strncmp(optarg, "call", 3) == 0)
219                                 parse_type = SCST_USER_PARSE_CALL;
220                         else if (strncmp(optarg, "excpt", 5) == 0)
221                                 parse_type = SCST_USER_PARSE_EXCEPTION;
222                         else
223                                 goto out_usage;
224                         break;
225                 case 'f':
226                         on_free_cmd_type_set = 1;
227                         if (strncmp(optarg, "ignore", 6) == 0)
228                                 on_free_cmd_type = SCST_USER_ON_FREE_CMD_IGNORE;
229                         else if (strncmp(optarg, "call", 3) == 0)
230                                 on_free_cmd_type = SCST_USER_ON_FREE_CMD_CALL;
231                         else
232                                 goto out_usage;
233                         break;
234                 case 'm':
235                         if (strncmp(optarg, "all", 3) == 0)
236                                 memory_reuse_type = SCST_USER_MEM_REUSE_ALL;
237                         else if (strncmp(optarg, "read", 4) == 0)
238                                 memory_reuse_type = SCST_USER_MEM_REUSE_READ;
239                         else if (strncmp(optarg, "write", 5) == 0)
240                                 memory_reuse_type = SCST_USER_MEM_REUSE_WRITE;
241                         else if (strncmp(optarg, "none", 4) == 0)
242                                 memory_reuse_type = SCST_USER_MEM_NO_REUSE;
243                         else
244                                 goto out_usage;
245                         break;
246                 case 'l':
247                         dev.non_blocking = 1;
248                         break;
249 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
250                 case 'g':
251                         dev.debug_tm_ignore = 1;
252                         break;
253 #endif
254                 case 'v':
255                         printf("%s version %s\n", app_name, VERSION_STR);
256                         goto out_done;
257                 default:
258                         goto out_usage;
259                 }
260         }
261
262         if (optind != (argc-2))
263                 goto out_usage;
264
265         if (!on_free_cmd_type_set &&
266             (memory_reuse_type != SCST_USER_MEM_REUSE_ALL))
267                 on_free_cmd_type = SCST_USER_ON_FREE_CMD_CALL;
268
269         dev.name = argv[optind];
270         dev.file_name = argv[optind+1];
271
272         TRACE_DBG("Opening file %s", dev.file_name);
273         fd = open(dev.file_name, O_RDONLY|O_LARGEFILE);
274         if (fd < 0) {
275                 res = -errno;
276                 PRINT_ERROR("Unable to open file %s (%s)", dev.file_name,
277                         strerror(-res));
278                 goto out_done;
279         }
280
281         dev.file_size = lseek64(fd, 0, SEEK_END);
282         dev.nblocks = dev.file_size >> dev.block_shift;
283
284         close(fd);
285
286         PRINT_INFO("Virtual device \"%s\", path \"%s\", size %"PRId64"Mb, "
287                 "block size %d, nblocks %"PRId64", options:", dev.name,
288                 dev.file_name, (uint64_t)dev.file_size/1024/1024,
289                 dev.block_size, (uint64_t)dev.nblocks);
290         if (dev.rd_only_flag)
291                 PRINT_INFO("    %s", "READ ONLY");
292         if (dev.wt_flag)
293                 PRINT_INFO("    %s", "WRITE THROUGH");
294         if (dev.nv_cache)
295                 PRINT_INFO("    %s", "NV_CACHE");
296         if (dev.o_direct_flag)
297                 PRINT_INFO("    %s", "O_DIRECT");
298         if (dev.nullio)
299                 PRINT_INFO("    %s", "NULLIO");
300         if (dev.non_blocking)
301                 PRINT_INFO("    %s", "NON-BLOCKING");
302
303         switch(parse_type) {
304         case SCST_USER_PARSE_STANDARD:
305                 PRINT_INFO("    %s", "Standard parse");
306                 break;
307         case SCST_USER_PARSE_CALL:
308                 PRINT_INFO("    %s", "Call parse");
309                 break;
310         case SCST_USER_PARSE_EXCEPTION:
311                 PRINT_INFO("    %s", "Exception parse");
312                 break;
313         default:
314                 sBUG();
315         }
316
317         switch(on_free_cmd_type) {
318         case SCST_USER_ON_FREE_CMD_IGNORE:
319                 PRINT_INFO("    %s", "Ignore on_free_cmd");
320                 break;
321         case SCST_USER_ON_FREE_CMD_CALL:
322                 PRINT_INFO("    %s", "Call on_free_cmd");
323                 break;
324         default:
325                 sBUG();
326         }
327
328         switch(memory_reuse_type) {
329         case SCST_USER_MEM_REUSE_ALL:
330                 PRINT_INFO("    %s", "Full memory reuse enabled");
331                 break;
332         case SCST_USER_MEM_REUSE_READ:
333                 PRINT_INFO("    %s", "READ memory reuse enabled");
334                 break;
335         case SCST_USER_MEM_REUSE_WRITE:
336                 PRINT_INFO("    %s", "WRITE memory reuse enabled");
337                 break;
338         case SCST_USER_MEM_NO_REUSE:
339                 PRINT_INFO("    %s", "Memory reuse disabled");
340                 break;
341         default:
342                 sBUG();
343         }
344
345         if (!dev.o_direct_flag && (memory_reuse_type == SCST_USER_MEM_NO_REUSE)) {
346                 PRINT_INFO("    %s", "Using unaligned buffers");
347                 dev.alloc_fn = malloc;
348         }
349
350 #if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
351         if (dev.debug_tm_ignore) {
352                 PRINT_INFO("    %s", "DEBUG_TM_IGNORE");
353         }
354 #endif
355
356 #ifdef DEBUG
357         PRINT_INFO("trace_flag %lx", trace_flag);
358 #endif
359
360         dev.scst_usr_fd = open(DEV_USER_PATH DEV_USER_NAME, O_RDWR |
361                 (dev.non_blocking ? O_NONBLOCK : 0));
362         if (dev.scst_usr_fd < 0) {
363                 res = -errno;
364                 PRINT_ERROR("Unable to open SCST device %s (%s)",
365                         DEV_USER_PATH DEV_USER_NAME, strerror(-res));
366                 goto out_done;
367         }
368
369         memset(&desc, 0, sizeof(desc));
370         desc.version_str = (unsigned long)DEV_USER_VERSION;
371         strncpy(desc.name, dev.name, sizeof(desc.name)-1);
372         desc.name[sizeof(desc.name)-1] = '\0';
373         desc.type = dev.type;
374         desc.block_size = dev.block_size;
375
376         desc.opt.parse_type = parse_type;
377         desc.opt.on_free_cmd_type = on_free_cmd_type;
378         desc.opt.memory_reuse_type = memory_reuse_type;
379
380         desc.opt.tst = SCST_CONTR_MODE_SEP_TASK_SETS;
381         desc.opt.queue_alg = SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER;
382
383         res = ioctl(dev.scst_usr_fd, SCST_USER_REGISTER_DEVICE, &desc);
384         if (res != 0) {
385                 res = errno;
386                 PRINT_ERROR("Unable to register device: %s", strerror(res));
387                 goto out_close;
388         }
389
390 #if 1
391         {
392                 /* Not needed, added here only as a test */
393                 struct scst_user_opt opt;
394
395                 res = ioctl(dev.scst_usr_fd, SCST_USER_GET_OPTIONS, &opt);
396                 if (res != 0) {
397                         res = errno;
398                         PRINT_ERROR("Unable to get options: %s", strerror(res));
399                         goto out_close;
400                 }
401
402                 opt.parse_type = parse_type;
403                 opt.on_free_cmd_type = on_free_cmd_type;
404                 opt.memory_reuse_type = memory_reuse_type;
405
406                 res = ioctl(dev.scst_usr_fd, SCST_USER_SET_OPTIONS, &opt);
407                 if (res != 0) {
408                         res = errno;
409                         PRINT_ERROR("Unable to set options: %s", strerror(res));
410                         goto out_close;
411                 }
412         }
413 #endif
414
415         res = pthread_mutex_init(&dev.dev_mutex, NULL);
416         if (res != 0) {
417                 res = errno;
418                 PRINT_ERROR("pthread_mutex_init() failed: %s", strerror(res));
419                 goto out_close;
420         }
421
422         {
423                 pthread_t thread[threads];
424                 int i, j, rc;
425                 void *rc1;
426
427                 for(i = 0; i < threads; i++) {
428                         rc = pthread_create(&thread[i], NULL, main_loop, &dev);
429                         if (rc != 0) {
430                                 res = errno;
431                                 PRINT_ERROR("pthread_create() failed: %s",
432                                         strerror(res));
433                                 break;
434                         }
435                 }
436
437                 j = i;
438                 for(i = 0; i < j; i++) {
439                         rc = pthread_join(thread[i], &rc1);
440                         if (rc != 0) {
441                                 res = errno;
442                                 PRINT_ERROR("pthread_join() failed: %s",
443                                         strerror(res));
444                         } else if (rc1 != NULL) {
445                                 res = (long)rc1;
446                                 PRINT_INFO("Thread %d exited, res %lx", i,
447                                         (long)rc1);
448                         } else
449                                 PRINT_INFO("Thread %d exited", i);
450                 }
451         }
452
453         pthread_mutex_destroy(&dev.dev_mutex);
454
455 out_close:
456         close(dev.scst_usr_fd);
457
458 out_done:
459         debug_done();
460
461 out:
462         return res;
463
464 out_usage:
465         usage();
466         goto out_done;
467 }