- Module scst_user and user space utility to test it added

- Support for per-target default security groups added
 - FILEIO made multithreaded
 - BLOCKIO made async
 - Other improvements, fixes and cleanups


git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@121 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Vladislav Bolkhovitin
2007-05-31 17:11:57 +00:00
parent 581cb5cd4f
commit 61b0176514
22 changed files with 6368 additions and 119 deletions
+79
View File
@@ -0,0 +1,79 @@
#
# SCSI target mid-level makefile
#
# Copyright (C) 2007 Vladislav Bolkhovitin <vst@vlnb.net>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation, version 2
# of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
SRCS_F = fileio.c common.c debug.c
OBJS_F = $(SRCS_F:.c=.o)
#SRCS_C =
#OBJS_C = $(SRCS_C:.c=.o)
SCST_INC_DIR := ../../scst/include
INSTALL_DIR := /usr/local/bin/scst
CFLAGS += -O2 -Wall -Wextra -Wno-unused-parameter -Wstrict-prototypes \
-I$(SCST_INC_DIR) -D_GNU_SOURCE -D__USE_FILE_OFFSET64 \
-D__USE_LARGEFILE64
PROGS = fileio_tgt
LIBS = -lpthread
CFLAGS += -DEXTRACHECKS
#CFLAGS += -DTRACING
CFLAGS += -DDEBUG -g
#CFLAGS += -DDEBUG_NOMEM
#CFLAGS += -DDEBUG_SENSE
#CFLAGS += -DDEBUG_TM_IGNORE
#CFLAGS += -DDEBUG_TM_IGNORE -DDEBUG_TM_FN_IGNORE
#CFLAGS += -DDEBUG_TM_IGNORE_ALL
all: $(PROGS)
fileio_tgt: .depend_f $(OBJS_F)
$(CC) $(OBJS_F) $(LIBS) -o $@
#cdrom_tgt: .depend_c $(OBJS_C)
# $(CC) $(OBJS_C) $(LIBS) -o $@
ifeq (.depend_f,$(wildcard .depend_f))
-include .depend_f
endif
#ifeq (.depend_c,$(wildcard .depend_c))
#-include .depend_c
#endif
%.o: %.c Makefile
$(CC) -c -o $(@) $(CFLAGS) $(<)
.depend_f:
$(CC) -M $(CFLAGS) $(SRCS_F) >$(@)
#.depend_c:
# $(CC) -M $(CFLAGS) $(SRCS_C) >$(@)
install: all
install -d $(INSTALL_DIR)
install -m 755 $(PROGS) $(INSTALL_DIR)
uninstall:
rm -f $(INSTALL_DIR)/$(PROGS)
rm -rf $(INSTALL_DIR)
clean:
rm -f *.o $(PROGS) .depend*
extraclean: clean
.PHONY: all install uninstall clean extraclean
+1710
View File
File diff suppressed because it is too large Load Diff
+121
View File
@@ -0,0 +1,121 @@
/*
* common.h
*
* Copyright (C) 2007 Vladislav Bolkhovitin <vst@vlnb.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, version 2
* of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <scst_user.h>
#include "debug.h"
/* 8 byte ASCII Vendor */
#define VENDOR "SCST_USR"
/* 4 byte ASCII Product Revision Level - left aligned */
#define FIO_REV " 096"
#define MAX_USN_LEN 20
#define INQ_BUF_SZ 128
#define EVPD 0x01
#define CMDDT 0x02
#define MSENSE_BUF_SZ 256
#define DBD 0x08 /* disable block descriptor */
#define WP 0x80 /* write protect */
#define DPOFUA 0x10 /* DPOFUA bit */
#define WCE 0x04 /* write cache enable */
#define PF 0x10 /* page format */
#define SP 0x01 /* save pages */
#define PS 0x80 /* parameter saveable */
#define BYTE 8
#define DEF_SECTORS_PER 63
struct vdisk_tgt_dev {
uint64_t sess_h;
/*
* last_write_cmd_queue_type accessed without protection since we are
* guaranteed that only commands of the same type per tgt_dev can be
* processed simultaneously
*/
int last_write_cmd_queue_type;
};
struct vdisk_dev {
int scst_usr_fd;
uint32_t block_size;
uint64_t nblocks;
int block_shift;
loff_t file_size; /* in bytes */
void *(*alloc_fn)(size_t size);
pthread_mutex_t dev_mutex;
/* Below flags and are protected by dev_mutex */
unsigned int rd_only_flag:1;
unsigned int wt_flag:1;
unsigned int nv_cache:1;
unsigned int o_direct_flag:1;
unsigned int media_changed:1;
unsigned int prevent_allow_medium_removal:1;
unsigned int nullio:1;
unsigned int cdrom_empty:1;
unsigned int non_blocking:1;
unsigned int prio_thr:1;
#if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
unsigned int debug_tm_ignore:1;
#if defined(DEBUG_TM_IGNORE_ALL)
volatile int debug_tm_ignore_all;
#endif
#endif
struct vdisk_tgt_dev tgt_devs[25];
char *name; /* Name of virtual device,
must be <= SCSI Model + 1 */
char *file_name; /* File name */
char *usn;
int type;
};
struct vdisk_cmd
{
int fd;
struct scst_user_get_cmd *cmd;
struct vdisk_dev *dev;
struct scst_user_reply_cmd *reply;
uint8_t sense[SCST_SENSE_BUFFERSIZE];
};
/*
* min()/max() macros that also do
* strict type-checking.. See the
* "unnecessary" pointer comparison.
*/
#define min(x,y) ({ \
typeof(x) _x = (x); \
typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x < _y ? _x : _y; })
#define max(x,y) ({ \
typeof(x) _x = (x); \
typeof(y) _y = (y); \
(void) (&_x == &_y); \
_x > _y ? _x : _y; })
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
void *main_loop(void *arg);
void *prio_loop(void *arg);
+120
View File
@@ -0,0 +1,120 @@
/*
* debug.c
*
* Copyright (C) 2004-2007 Vladislav Bolkhovitin <vst@vlnb.net>
* and Leonid Stoljar
*
* Contains helper functions for execution tracing and error reporting.
* Intended to be included in main .c file.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, version 2
* of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <stdio.h>
#include <time.h>
#include "debug.h"
_syscall0(pid_t,gettid);
#if defined(DEBUG) || defined(TRACING)
#define TRACE_BUF_SIZE 512
static char trace_buf[TRACE_BUF_SIZE];
//static spinlock_t trace_buf_lock = SPIN_LOCK_UNLOCKED;
int debug_print_prefix(unsigned long trace_flag, const char *func,
int line)
{
int i = 0;
// spin_lock_irqsave(&trace_buf_lock, flags);
if (trace_flag & TRACE_TIME) {
struct tm t;
time_t tt;
time(&tt);
localtime_r(&tt, &t);
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE, "%d:%d:%d ",
t.tm_hour, t.tm_min, t.tm_sec);
}
if (trace_flag & TRACE_PID)
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE, "[%d]: ",
gettid());
if (trace_flag & TRACE_FUNCTION)
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%s:", func);
if (trace_flag & TRACE_LINE)
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%i:", line);
if (i > 0)
PRINTN("%s", trace_buf);
// spin_unlock_irqrestore(&trace_buf_lock, flags);
return i;
}
void debug_print_buffer(const void *data, int len)
{
int z, z1, i;
const unsigned char *buf = (const unsigned char *) data;
int f = 0;
if (buf == NULL)
return;
// spin_lock_irqsave(&trace_buf_lock, flags);
PRINT(" (h)___0__1__2__3__4__5__6__7__8__9__A__B__C__D__E__F");
for (z = 0, z1 = 0, i = 0; z < len; z++) {
if (z % 16 == 0) {
if (z != 0) {
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i,
" ");
for (; (z1 < z) && (i < TRACE_BUF_SIZE - 1);
z1++) {
if ((buf[z1] >= 0x20) &&
(buf[z1] < 0x80))
trace_buf[i++] = buf[z1];
else
trace_buf[i++] = '.';
}
trace_buf[i] = '\0';
PRINT("%s", trace_buf);
i = 0;
f = 1;
}
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i,
"%4x: ", z);
}
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%02x ",
buf[z]);
}
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, " ");
for (; (z1 < z) && (i < TRACE_BUF_SIZE - 1); z1++) {
if ((buf[z1] > 0x20) && (buf[z1] < 0x80))
trace_buf[i++] = buf[z1];
else
trace_buf[i++] = '.';
}
trace_buf[i] = '\0';
if (f) {
PRINT("%s", trace_buf)
} else {
PRINT("%s", trace_buf);
}
// spin_unlock_irqrestore(&trace_buf_lock, flags);
return;
}
#endif /* DEBUG || TRACING */
+260
View File
@@ -0,0 +1,260 @@
/*
* debug.h
*
* Copyright (C) 2004-2007 Vladislav Bolkhovitin <vst@vlnb.net>
* and Leonid Stoljar
*
* Contains macroses for execution tracing and error reporting
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, version 2
* of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __DEBUG_H
#define __DEBUG_H
#include <sys/types.h>
#include <linux/unistd.h>
#include <errno.h>
extern pid_t gettid(void);
#define sBUG() assert(0)
#define sBUG_ON(p) assert(!(p))
#ifdef EXTRACHECKS
#define EXTRACHECKS_BUG_ON(a) sBUG_ON(a)
#else
#define EXTRACHECKS_BUG_ON(a)
#endif
#define TRACE_NULL 0x00000000
#define TRACE_DEBUG 0x00000001
#define TRACE_FUNCTION 0x00000002
#define TRACE_LINE 0x00000004
#define TRACE_PID 0x00000008
#define TRACE_ENTRYEXIT 0x00000010
#define TRACE_BUFF 0x00000020
#define TRACE_MEMORY 0x00000040
#define TRACE_SG 0x00000080
#define TRACE_OUT_OF_MEM 0x00000100
#define TRACE_MINOR 0x00000200 /* less important events */
#define TRACE_MGMT 0x00000400
#define TRACE_MGMT_DEBUG 0x00000800
#define TRACE_SCSI 0x00001000
#define TRACE_SPECIAL 0x00002000 /* filtering debug, etc */
#define TRACE_TIME 0x00004000
#define TRACE_ORDER 0x00008000
#define TRACE_ALL 0xffffffff
#define PRINT(format, args...) fprintf(stdout, format "\n", ## args);
#define PRINTN(format, args...) fprintf(stdout, format, ## args);
extern char *app_name;
#define LOG_PREFIX app_name
#if defined(DEBUG) || defined(TRACING)
extern unsigned long trace_flag;
extern int debug_print_prefix(unsigned long trace_flag, const char *func, int line);
extern void debug_print_buffer(const void *data, int len);
#define TRACE(trace, format, args...) \
do { \
if (trace_flag & (trace)) \
{ \
debug_print_prefix(trace_flag, __FUNCTION__, __LINE__); \
PRINT(format, args); \
} \
} while(0)
#define TRACE_BUFFER(message, buff, len) \
do { \
if (trace_flag & TRACE_BUFF) \
{ \
debug_print_prefix(trace_flag, __FUNCTION__, __LINE__); \
PRINT("%s:", message); \
debug_print_buffer(buff, len); \
} \
} while(0)
#define TRACE_BUFF_FLAG(flag, message, buff, len) \
do { \
if (trace_flag & (flag)) \
{ \
debug_print_prefix(trace_flag, __FUNCTION__, __LINE__); \
PRINT("%s:", message); \
debug_print_buffer(buff, len); \
} \
} while(0)
#else /* DEBUG || TRACING */
#define TRACE(trace, args...) {}
#define TRACE_BUFFER(message, buff, len) {}
#define TRACE_BUFF_FLAG(flag, message, buff, len) {}
#endif /* DEBUG || TRACING */
#ifdef DEBUG
#include <assert.h>
#define TRACE_MEM(format, args...) \
do { \
if (trace_flag & TRACE_MEMORY) \
{ \
debug_print_prefix(trace_flag, __FUNCTION__, __LINE__); \
PRINT(format, args); \
} \
} while(0)
#define TRACE_DBG(format, args...) \
do { \
if (trace_flag & TRACE_DEBUG) \
{ \
debug_print_prefix(trace_flag, __FUNCTION__, __LINE__); \
PRINT(format, args); \
} \
} while(0)
#define TRACE_MGMT_DBG(format, args...) \
do { \
if (trace_flag & TRACE_MGMT_DEBUG) \
{ \
debug_print_prefix(trace_flag, __FUNCTION__, __LINE__); \
PRINT(format, args); \
} \
} while(0)
#define PRINT_ERROR_PR(format, args...) \
do { \
TRACE(trace_flag, "%s: ***ERROR*** " format, \
LOG_PREFIX, args); \
} while(0)
#define PRINT_INFO_PR(format, args...) \
do { \
TRACE(trace_flag, "%s: " \
format, LOG_PREFIX, args); \
} while(0)
#define PRINT_ERROR(format, args...) \
do { \
TRACE(trace_flag, "***ERROR*** " format, args); \
} while(0)
#define PRINT_INFO(format, args...) \
do { \
TRACE(trace_flag, format, args); \
} while(0)
#define TRACE_ENTRY() \
do { \
if (trace_flag & TRACE_ENTRYEXIT) \
{ \
if (trace_flag & TRACE_PID) \
{ \
PRINT("[%d]: ENTRY %s", gettid(), \
__FUNCTION__); \
} \
else \
{ \
PRINT("ENTRY %s", __FUNCTION__); \
} \
} \
} while(0)
#define TRACE_EXIT() \
do { \
if (trace_flag & TRACE_ENTRYEXIT) \
{ \
if (trace_flag & TRACE_PID) \
{ \
PRINT("[%d]: EXIT %s", gettid(), \
__FUNCTION__); \
} \
else \
{ \
PRINT("EXIT %s", __FUNCTION__); \
} \
} \
} while(0)
#define TRACE_EXIT_RES(res) \
do { \
if (trace_flag & TRACE_ENTRYEXIT) \
{ \
if (trace_flag & TRACE_PID) \
{ \
PRINT("[%d]: EXIT %s: %ld", gettid(), \
__FUNCTION__, (long)(res)); \
} \
else \
{ \
PRINT("EXIT %s: %ld", __FUNCTION__, (long)(res)); \
} \
} \
} while(0)
#define TRACE_EXIT_HRES(res) \
do { \
if (trace_flag & TRACE_ENTRYEXIT) \
{ \
if (trace_flag & TRACE_PID) \
{ \
PRINT("[%d]: EXIT %s: 0x%lx", gettid(), \
__FUNCTION__, (long)(res)); \
} \
else \
{ \
PRINT("EXIT %s: %lx", __FUNCTION__, (long)(res)); \
} \
} \
} while(0)
#else /* DEBUG */
#define NDEBUG
#include <assert.h>
#define TRACE_MEM(format, args...) {}
#define TRACE_DBG(format, args...) {}
#define TRACE_MGMT_DBG(format, args...) {}
#define TRACE_ENTRY() {}
#define TRACE_EXIT() {}
#define TRACE_EXIT_RES(res) {}
#define TRACE_EXIT_HRES(res) {}
#define PRINT_INFO_PR(format, args...) \
do { \
PRINT("%s: " format, LOG_PREFIX, args); \
} while(0)
#define PRINT_ERROR_PR(format, args...) \
do { \
PRINT("%s: ***ERROR*** " \
format, LOG_PREFIX, args); \
} while(0)
#define PRINT_INFO(format, args...) \
do { \
PRINT(format, args); \
} while(0)
#define PRINT_ERROR(format, args...) \
do { \
PRINT("***ERROR*** " format, args); \
} while(0)
#endif /* DEBUG */
#endif /* __DEBUG_H */
+488
View File
@@ -0,0 +1,488 @@
/*
* fileio.c
*
* Copyright (C) 2007 Vladislav Bolkhovitin <vst@vlnb.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, version 2
* of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <getopt.h>
#include <malloc.h>
#include <sys/types.h>
#include <sys/user.h>
#include <sys/poll.h>
#include <sys/ioctl.h>
#include <pthread.h>
char *app_name;
#define LOG_PREFIX app_name
#include "common.h"
#if defined(DEBUG) || defined(TRACING)
#ifdef DEBUG
/*#define DEFAULT_LOG_FLAGS (TRACE_ALL & ~TRACE_MEMORY & ~TRACE_BUFF \
& ~TRACE_FUNCTION)
#define DEFAULT_LOG_FLAGS (TRACE_ALL & ~TRACE_MEMORY & ~TRACE_BUFF & \
~TRACE_SCSI & ~TRACE_SCSI_SERIALIZING & ~TRACE_DEBUG)
*/
#define DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MINOR | TRACE_PID | \
TRACE_FUNCTION | TRACE_SPECIAL | TRACE_MGMT | TRACE_MGMT_DEBUG | TRACE_ORDER | \
TRACE_TIME)
#define TRACE_SN(args...) TRACE(TRACE_SCSI_SERIALIZING, args)
#else /* DEBUG */
# ifdef TRACING
#define DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MINOR | TRACE_PID | \
TRACE_TIME | TRACE_SPECIAL)
# else
#define DEFAULT_LOG_FLAGS 0
# endif
#endif /* DEBUG */
unsigned long trace_flag = DEFAULT_LOG_FLAGS;
#endif /* defined(DEBUG) || defined(TRACING) */
#define DEF_BLOCK_SHIFT 9
#define VERSION_STR "0.9.6"
#define THREADS 7
static struct option const long_options[] =
{
{"block", required_argument, 0, 'b'},
{"threads", required_argument, 0, 'e'},
{"write_through", no_argument, 0, 't'},
{"read_only", no_argument, 0, 'r'},
{"direct", no_argument, 0, 'o'},
{"nullio", no_argument, 0, 'n'},
{"nv_cache", no_argument, 0, 'c'},
{"parse", required_argument, 0, 'p'},
{"on_free", required_argument, 0, 'f'},
{"mem_reuse", required_argument, 0, 'm'},
{"prio_thread", no_argument, 0, 's'},
{"non_blocking", no_argument, 0, 'l'},
#if defined(DEBUG) || defined(TRACING)
{"debug", required_argument, 0, 'd'},
#endif
#if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
{"debug_tm_ignore", no_argument, 0, 'g'},
#endif
{"version", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0},
};
static void usage(void)
{
printf("Usage: %s [OPTION] name path\n", app_name);
printf("\nFILEIO disk target emulator for SCST\n");
printf(" -b, --block=size Block size, must be power of 2 and >=512\n");
printf(" -e, --threads=count Number of threads, %d by default\n", THREADS);
printf(" -t, --write_through Write through mode\n");
printf(" -r, --read_only Read only\n");
printf(" -o, --direct O_DIRECT mode\n");
printf(" -n, --nullio NULLIO mode\n");
printf(" -c, --nv_cache NV_CACHE mode\n");
printf(" -p, --parse=type Parse type, one of \"std\" "
"(default), \"call\" or \"excpt\"\n");
printf(" -f, --on_free=type On free call type, one of \"ignore\" "
"(default) or \"call\"\n");
printf(" -m, --mem_reuse=type Memory reuse type, one of \"all\" "
"(default), \"read\", \"write\" or \"none\"\n");
printf(" -s, --prio_thread Use separate thread for mgmt (prio) commands\n");
printf(" -l, --non_blocking Use non-blocking operations\n");
#if defined(DEBUG) || defined(TRACING)
printf(" -d, --debug=level Debug tracing level\n");
#endif
#if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
printf(" -g, --debug_tm_ignore Turn on DEBUG_TM_IGNORE\n");
#endif
return;
}
static int scst_calc_block_shift(int sector_size)
{
int block_shift = 0;
int t = sector_size;
if (sector_size == 0)
goto out;
t = sector_size;
while(1) {
if ((t & 1) != 0)
break;
t >>= 1;
block_shift++;
}
if (block_shift < 9) {
PRINT_ERROR_PR("Wrong sector size %d", sector_size);
block_shift = -1;
}
out:
TRACE_EXIT_RES(block_shift);
return block_shift;
}
static void *align_alloc(size_t size)
{
return memalign(PAGE_SIZE, size);
}
int main(int argc, char **argv)
{
int res = 0;
int ch, longindex;
int fd;
int parse_type = SCST_USER_PARSE_STANDARD;
int on_free_cmd_type = SCST_USER_ON_FREE_CMD_IGNORE;
int on_free_cmd_type_set = 0;
int memory_reuse_type = SCST_USER_MEM_REUSE_ALL;
int threads = THREADS;
struct scst_user_dev_desc desc;
struct vdisk_dev dev;
setlinebuf(stdout);
app_name = argv[0];
memset(&dev, 0, sizeof(dev));
dev.block_size = (1 << DEF_BLOCK_SHIFT);
dev.block_shift = DEF_BLOCK_SHIFT;
dev.type = TYPE_DISK;
dev.alloc_fn = align_alloc;
while ((ch = getopt_long(argc, argv, "+b:e:tronsglcp:f:m:d:vh", long_options,
&longindex)) >= 0) {
switch (ch) {
case 'b':
dev.block_size = atoi(optarg);
PRINT_INFO("block_size %x (%s)", dev.block_size, optarg);
dev.block_shift = scst_calc_block_shift(dev.block_size);
if (dev.block_shift < 9) {
res = -EINVAL;
goto out_usage;
}
break;
case 'e':
threads = strtol(optarg, (char **)NULL, 0);
break;
case 't':
dev.wt_flag = 1;
break;
#if defined(DEBUG) || defined(TRACING)
case 'd':
trace_flag = strtol(optarg, (char **)NULL, 0);
break;
#endif
case 'r':
dev.rd_only_flag = 1;
break;
case 'o':
dev.o_direct_flag = 1;
dev.alloc_fn = align_alloc;
break;
case 'n':
dev.nullio = 1;
break;
case 'c':
dev.nv_cache = 1;
break;
case 'p':
if (strncmp(optarg, "std", 3) == 0)
parse_type = SCST_USER_PARSE_STANDARD;
else if (strncmp(optarg, "call", 3) == 0)
parse_type = SCST_USER_PARSE_CALL;
else if (strncmp(optarg, "excpt", 5) == 0)
parse_type = SCST_USER_PARSE_EXCEPTION;
else
goto out_usage;
break;
case 'f':
on_free_cmd_type_set = 1;
if (strncmp(optarg, "ignore", 6) == 0)
on_free_cmd_type = SCST_USER_ON_FREE_CMD_IGNORE;
else if (strncmp(optarg, "call", 3) == 0)
on_free_cmd_type = SCST_USER_ON_FREE_CMD_CALL;
else
goto out_usage;
break;
case 'm':
if (strncmp(optarg, "all", 3) == 0)
memory_reuse_type = SCST_USER_MEM_REUSE_ALL;
else if (strncmp(optarg, "read", 4) == 0)
memory_reuse_type = SCST_USER_MEM_REUSE_READ;
else if (strncmp(optarg, "write", 5) == 0)
memory_reuse_type = SCST_USER_MEM_REUSE_WRITE;
else if (strncmp(optarg, "none", 4) == 0)
memory_reuse_type = SCST_USER_MEM_NO_REUSE;
else
goto out_usage;
break;
case 's':
dev.prio_thr = 1;
break;
case 'l':
dev.non_blocking = 1;
break;
#if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
case 'g':
dev.debug_tm_ignore = 1;
break;
#endif
case 'v':
printf("%s version %s\n", app_name, VERSION_STR);
goto out;
default:
goto out_usage;
}
}
if (optind != (argc-2))
goto out_usage;
if (!on_free_cmd_type_set &&
(memory_reuse_type != SCST_USER_MEM_REUSE_ALL))
on_free_cmd_type = SCST_USER_ON_FREE_CMD_CALL;
dev.name = argv[optind];
dev.file_name = argv[optind+1];
TRACE_DBG("Opening file %s", dev.file_name);
fd = open(dev.file_name, O_RDONLY|O_LARGEFILE);
if (fd < 0) {
res = errno;
PRINT_ERROR_PR("Unable to open file %s (%s)", dev.file_name,
strerror(res));
goto out;
}
dev.file_size = lseek64(fd, 0, SEEK_END);
dev.nblocks = dev.file_size >> dev.block_shift;
close(fd);
PRINT_INFO_PR("Virtual device \"%s\", path \"%s\", size %LdMb, "
"block size %d, nblocks %Ld, options:", dev.name, dev.file_name,
dev.file_size/1024/1024, dev.block_size, dev.nblocks);
if (dev.rd_only_flag)
PRINT_INFO(" %s", "READ ONLY");
if (dev.wt_flag)
PRINT_INFO(" %s", "WRITE THROUGH");
if (dev.nv_cache)
PRINT_INFO(" %s", "NV_CACHE");
if (dev.o_direct_flag)
PRINT_INFO(" %s", "O_DIRECT");
if (dev.nullio)
PRINT_INFO(" %s", "NULLIO");
if (dev.non_blocking)
PRINT_INFO(" %s", "NON-BLOCKING");
switch(parse_type) {
case SCST_USER_PARSE_STANDARD:
PRINT_INFO(" %s", "Standard parse");
break;
case SCST_USER_PARSE_CALL:
PRINT_INFO(" %s", "Call parse");
break;
case SCST_USER_PARSE_EXCEPTION:
PRINT_INFO(" %s", "Exception parse");
break;
default:
sBUG();
}
switch(on_free_cmd_type) {
case SCST_USER_ON_FREE_CMD_IGNORE:
PRINT_INFO(" %s", "Ignore on_free_cmd");
break;
case SCST_USER_ON_FREE_CMD_CALL:
PRINT_INFO(" %s", "Call on_free_cmd");
break;
default:
sBUG();
}
switch(memory_reuse_type) {
case SCST_USER_MEM_REUSE_ALL:
PRINT_INFO(" %s", "Full memory reuse enabled");
break;
case SCST_USER_MEM_REUSE_READ:
PRINT_INFO(" %s", "READ memory reuse enabled");
break;
case SCST_USER_MEM_REUSE_WRITE:
PRINT_INFO(" %s", "WRITE memory reuse enabled");
break;
case SCST_USER_MEM_NO_REUSE:
PRINT_INFO(" %s", "Memory reuse disabled");
break;
default:
sBUG();
}
if (!dev.o_direct_flag && (memory_reuse_type == SCST_USER_MEM_NO_REUSE)) {
PRINT_INFO(" %s", "Using unaligned buffers");
dev.alloc_fn = malloc;
}
if (dev.prio_thr) {
PRINT_INFO(" %s", "Using separate prio thread");
}
#if defined(DEBUG_TM_IGNORE) || defined(DEBUG_TM_IGNORE_ALL)
if (dev.debug_tm_ignore) {
PRINT_INFO(" %s", "DEBUG_TM_IGNORE");
}
#endif
#ifdef DEBUG
PRINT_INFO("trace_flag %lx", trace_flag);
#endif
dev.scst_usr_fd = open(DEV_USER_PATH DEV_USER_NAME, O_RDWR |
(dev.non_blocking ? O_NONBLOCK : 0));
if (dev.scst_usr_fd < 0) {
res = dev.scst_usr_fd;
PRINT_ERROR_PR("Unable to open SCST device %s (%s)",
DEV_USER_PATH DEV_USER_NAME, strerror(res));
goto out;
}
memset(&desc, 0, sizeof(desc));
desc.version = DEV_USER_VERSION;
strncpy(desc.name, dev.name, sizeof(desc.name)-1);
desc.name[sizeof(desc.name)-1] = '\0';
desc.type = dev.type;
desc.block_size = dev.block_size;
desc.opt.parse_type = parse_type;
desc.opt.on_free_cmd_type = on_free_cmd_type;
desc.opt.memory_reuse_type = memory_reuse_type;
if (dev.prio_thr)
desc.opt.prio_queue_type = SCST_USER_PRIO_QUEUE_SEPARATE;
else
desc.opt.prio_queue_type = SCST_USER_PRIO_QUEUE_SINGLE;
res = ioctl(dev.scst_usr_fd, SCST_USER_REGISTER_DEVICE, &desc);
if (res != 0) {
res = errno;
PRINT_ERROR_PR("Unable to register device: %s", strerror(res));
goto out_close;
}
#if 0
{
/* Not needed, added here only as a test */
struct scst_user_opt opt;
res = ioctl(dev.scst_usr_fd, SCST_USER_GET_OPTIONS, &opt);
if (res != 0) {
res = errno;
PRINT_ERROR_PR("Unable to get options: %s", strerror(res));
goto out_close;
}
opt.parse_type = parse_type;
opt.on_free_cmd_type = on_free_cmd_type;
opt.memory_reuse_type = memory_reuse_type;
if (dev.prio_thr)
opt.prio_queue_type = SCST_USER_PRIO_QUEUE_SEPARATE;
else
opt.prio_queue_type = SCST_USER_PRIO_QUEUE_SINGLE;
res = ioctl(dev.scst_usr_fd, SCST_USER_SET_OPTIONS, &opt);
if (res != 0) {
res = errno;
PRINT_ERROR_PR("Unable to get options: %s", strerror(res));
goto out_close;
}
}
#endif
res = pthread_mutex_init(&dev.dev_mutex, NULL);
if (res != 0) {
res = errno;
PRINT_ERROR_PR("pthread_mutex_init() failed: %s", strerror(res));
goto out_close;
}
{
pthread_t thread[threads];
pthread_t prio;
int i, j, rc;
void *rc1;
for(i = 0; i < threads; i++) {
rc = pthread_create(&thread[i], NULL, main_loop, &dev);
if (rc != 0) {
res = errno;
PRINT_ERROR_PR("pthread_create() failed: %s",
strerror(res));
break;
}
}
if (dev.prio_thr) {
rc = pthread_create(&prio, NULL, prio_loop, &dev);
if (rc != 0) {
res = errno;
PRINT_ERROR_PR("Prio pthread_create() failed: %s",
strerror(res));
dev.prio_thr = 0;
}
}
j = i;
for(i = 0; i < j; i++) {
rc = pthread_join(thread[i], &rc1);
if (rc != 0) {
res = errno;
PRINT_ERROR_PR("pthread_join() failed: %s",
strerror(res));
} else if (rc1 != NULL)
res = (int)rc1;
}
if (dev.prio_thr) {
rc = pthread_join(prio, &rc1);
if (rc != 0) {
res = errno;
PRINT_ERROR_PR("Prio pthread_join() failed: %s",
strerror(res));
} else if (rc1 != NULL)
res = (int)rc1;
}
}
pthread_mutex_destroy(&dev.dev_mutex);
out_close:
close(dev.scst_usr_fd);
out:
return res;
out_usage:
usage();
goto out;
}