Explicit ALUA user space

git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@6579 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Vladislav Bolkhovitin
2015-11-06 03:40:37 +00:00
parent 2ecfcdc643
commit ae38d284e0
13 changed files with 1066 additions and 288 deletions

View File

@@ -45,7 +45,7 @@ QLA_DIR=qla2x00t_git/qla2x00-target
QLA_OLD_INI_DIR=qla2x00t
QLA_OLD_DIR=qla2x00t/qla2x00-target
LSI_DIR=mpt
USR_DIR=usr/fileio
USR_DIR=usr
SRP_DIR=srpt
SCST_LOCAL_DIR=scst_local
MVSAS_DIR=mvsas_tgt
@@ -129,7 +129,7 @@ help:
@echo " scst_local_install : scst_local target: install"
@echo " scst_local_uninstall : scst_local target: uninstall"
@echo ""
@echo " usr : make user space fileio_tgt target"
@echo " usr : make user space targets"
@echo " usr_clean : usr target: clean "
@echo " usr_extraclean : usr target: clean + clean dependencies"
@echo " usr_install : usr target: install"

78
usr/Makefile Normal file
View File

@@ -0,0 +1,78 @@
#
# Common makefile for SCSI target mid-level and its drivers
#
# Copyright (C) 2004 - 2015 Vladislav Bolkhovitin <vst@vlnb.net>
# Copyright (C) 2004 - 2005 Leonid Stoljar
# Copyright (C) 2007 - 2015 SanDisk Corporation
#
# 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.
#
#
SHELL=/bin/bash
# Decide to use which kernel src. If not specified, is current running kernel.
#export KDIR=/usr/src/linux-2.6
FILEIO_DIR=fileio
STPGD_DIR=stpgd
all:
cd $(FILEIO_DIR) && $(MAKE) $@
cd $(STPGD_DIR) && $(MAKE) $@
install:
cd $(FILEIO_DIR) && $(MAKE) $@
cd $(STPGD_DIR) && $(MAKE) $@
uninstall:
cd $(FILEIO_DIR) && $(MAKE) $@
cd $(STPGD_DIR) && $(MAKE) $@
clean:
cd $(FILEIO_DIR) && $(MAKE) $@
cd $(STPGD_DIR) && $(MAKE) $@
extraclean:
cd $(FILEIO_DIR) && $(MAKE) $@
cd $(STPGD_DIR) && $(MAKE) $@
2release:
cd $(FILEIO_DIR) && $(MAKE) $@
cd $(STPGD_DIR) && $(MAKE) $@
2debug:
cd $(FILEIO_DIR) && $(MAKE) $@
cd $(STPGD_DIR) && $(MAKE) $@
2perf:
cd $(FILEIO_DIR) && $(MAKE) $@
cd $(STPGD_DIR) && $(MAKE) $@
disable_proc:
cd $(FILEIO_DIR) && $(MAKE) $@
cd $(STPGD_DIR) && $(MAKE) $@
enable_proc:
cd $(FILEIO_DIR) && $(MAKE) $@
cd $(STPGD_DIR) && $(MAKE) $@
help:
@echo " all (the default) : make all"
@echo " clean : clean files"
@echo " extraclean : clean + clean dependencies"
@echo " install : install"
@echo " uninstall : uninstall"
@echo " Notes :"
@echo " - install and uninstall must be made as root."
@echo " - be sure to compile qla against the correct initiator"
@echo " driver. Read its README for details."
.PHONY: all install uninstall clean extraclean help 2release 2debug 2perf disable_proc enable_proc

View File

@@ -29,10 +29,11 @@ OBJS_F = $(SRCS_F:.c=.o)
SCST_INC_DIR := $(shell if [ -e "$$PWD/../../scst" ]; \
then echo "$$PWD/../../scst/include"; \
else echo "$(DESTDIR)$(PREFIX)/include/scst"; fi)
DEBUG_INC_DIR := ../include
INSTALL_DIR := $(DESTDIR)$(PREFIX)/bin/scst
CFLAGS += -O2 -Wall -Wextra -Wno-unused-parameter -Wstrict-prototypes \
-I$(SCST_INC_DIR) -D_GNU_SOURCE -D__USE_FILE_OFFSET64 \
-I$(SCST_INC_DIR) -I$(DEBUG_INC_DIR) -D_GNU_SOURCE -D__USE_FILE_OFFSET64 \
-D__USE_LARGEFILE64
PROGS = fileio_tgt
LIBS = -lpthread

View File

@@ -1,148 +1 @@
/*
* debug.c
*
* Copyright (C) 2004 - 2015 Vladislav Bolkhovitin <vst@vlnb.net>
* Copyright (C) 2004 - 2005 Leonid Stoljar
* Copyright (C) 2007 - 2015 SanDisk Corporation
*
* 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 <pthread.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
#include "debug.h"
pid_t gettid (void)
{
return syscall(__NR_gettid);
}
#if defined(DEBUG) || defined(TRACING)
#define TRACE_BUF_SIZE 512
static char trace_buf[TRACE_BUF_SIZE];
static pthread_spinlock_t trace_buf_lock;
int debug_print_prefix(unsigned long trace_flag, const char *prefix,
const char *func, int line)
{
int i = 0;
pthread_spin_lock(&trace_buf_lock);
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 - i, "%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 - i, "[%d]: ",
gettid());
if (prefix != NULL)
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%s:", prefix);
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);
pthread_spin_unlock(&trace_buf_lock);
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;
pthread_spin_lock(&trace_buf_lock);
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);
}
pthread_spin_unlock(&trace_buf_lock);
return;
}
int debug_init(void)
{
int res;
res = pthread_spin_init(&trace_buf_lock, PTHREAD_PROCESS_PRIVATE);
if (res != 0) {
res = errno;
PRINT_ERROR("pthread_spin_init() failed: %s", strerror(res));
}
return res;
}
void debug_done(void)
{
pthread_spin_destroy(&trace_buf_lock);
}
#endif /* DEBUG || TRACING */
#include "../include/debug.c"

View File

@@ -25,6 +25,7 @@
#include <stdint.h>
#include <getopt.h>
#include <malloc.h>
#include <stdbool.h>
#include <inttypes.h>
#include <signal.h>
#include <sys/types.h>
@@ -36,7 +37,9 @@
char *app_name;
#include "version.h"
#include "common.h"
#include "debug.h"
#if defined(DEBUG) || defined(TRACING)
@@ -62,11 +65,11 @@ char *app_name;
# endif
#endif /* DEBUG */
bool log_daemon = false;
unsigned long trace_flag = DEFAULT_LOG_FLAGS;
#endif /* defined(DEBUG) || defined(TRACING) */
#define DEF_BLOCK_SHIFT 9
#define VERSION_STR "3.1.0-pre1"
#define THREADS 7
#define MAX_VDEVS 10

147
usr/include/debug.c Normal file
View File

@@ -0,0 +1,147 @@
/*
* debug.c
*
* Copyright (C) 2004 - 2015 Vladislav Bolkhovitin <vst@vlnb.net>
* Copyright (C) 2004 - 2005 Leonid Stoljar
* Copyright (C) 2007 - 2015 SanDisk Corporation
*
* 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 <pthread.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
#include "debug.h"
pid_t gettid(void)
{
return syscall(__NR_gettid);
}
#if defined(DEBUG) || defined(TRACING)
#define TRACE_BUF_SIZE 512
static char trace_buf[TRACE_BUF_SIZE];
static pthread_spinlock_t trace_buf_lock;
int debug_print_prefix(unsigned long trace_flag, const char *prefix,
const char *func, int line)
{
int i = 0;
pthread_spin_lock(&trace_buf_lock);
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 - i, "%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 - i, "[%d]: ",
gettid());
if (prefix != NULL)
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%s:", prefix);
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(LOG_INFO, "%s", trace_buf);
pthread_spin_unlock(&trace_buf_lock);
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;
pthread_spin_lock(&trace_buf_lock);
PRINT(LOG_INFO, " (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(LOG_INFO, "%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(LOG_INFO, "%s", trace_buf);
else
PRINT(LOG_INFO, "%s", trace_buf);
pthread_spin_unlock(&trace_buf_lock);
}
int debug_init(void)
{
int res;
res = pthread_spin_init(&trace_buf_lock, PTHREAD_PROCESS_PRIVATE);
if (res != 0) {
res = errno;
PRINT_ERROR("pthread_spin_init() failed: %s", strerror(res));
}
return res;
}
void debug_done(void)
{
pthread_spin_destroy(&trace_buf_lock);
}
#endif /* DEBUG || TRACING */

View File

@@ -24,6 +24,8 @@
#include <sys/types.h>
#include <linux/unistd.h>
#include <errno.h>
#include <syslog.h>
#include <stdbool.h>
extern pid_t gettid(void);
@@ -55,8 +57,21 @@ extern pid_t gettid(void);
#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);
#define PRINT(priority, format, args...) \
do { \
if (log_daemon) \
syslog(priority, format "\n", ## args); \
else \
fprintf(stdout, format "\n", ## args); \
} while (0)
#define PRINTN(priority, format, args...) \
do { \
if (log_daemon) \
syslog(priority, format, ## args); \
else \
fprintf(stdout, format, ## args); \
} while (0)
extern char *app_name;
#define LOG_PREFIX app_name
@@ -69,6 +84,7 @@ extern char *app_name;
#if defined(DEBUG) || defined(TRACING)
extern bool log_daemon;
extern unsigned long trace_flag;
extern int debug_init(void);
@@ -80,24 +96,23 @@ extern void debug_done(void);
*/
extern int debug_print_prefix(unsigned long trace_flag, const char *prefix,
const char *func, int line);
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, __LOG_PREFIX, __FUNCTION__, \
__LINE__); \
PRINT(format, args); \
} \
} while(0)
if (trace_flag & (trace)) { \
debug_print_prefix(trace_flag, __LOG_PREFIX, \
__func__, __LINE__); \
PRINT(LOG_DEBUG, format, args); \
} \
} while (0)
#define PRINT_BUFFER(message, buff, len) \
do { \
PRINT("%s:", message); \
debug_print_buffer(buff, len); \
} while(0)
PRINT(LOG_INFO, "%s:", message); \
debug_print_buffer(buff, len); \
} while (0)
#else /* DEBUG || TRACING */
@@ -115,142 +130,126 @@ static inline void debug_done(void) {}
#define TRACE_MEM(format, args...) \
do { \
if (trace_flag & TRACE_MEMORY) \
{ \
debug_print_prefix(trace_flag, NULL, __FUNCTION__, \
__LINE__); \
PRINT(format, args); \
} \
} while(0)
if (trace_flag & TRACE_MEMORY) { \
debug_print_prefix(trace_flag, NULL, \
__func__, __LINE__); \
PRINT(LOG_DEBUG, format, args); \
} \
} while (0)
#define TRACE_DBG(format, args...) \
do { \
if (trace_flag & TRACE_DEBUG) \
{ \
debug_print_prefix(trace_flag, NULL, __FUNCTION__, \
__LINE__); \
PRINT(format, args); \
} \
} while(0)
if (trace_flag & TRACE_DEBUG) { \
debug_print_prefix(trace_flag, NULL, \
__func__, __LINE__); \
PRINT(LOG_DEBUG, format, args); \
} \
} while (0)
#define TRACE_DBG_SPECIAL(args...) TRACE(TRACE_DEBUG|TRACE_SPECIAL, args)
#define TRACE_MGMT_DBG(format, args...) \
do { \
if (trace_flag & TRACE_MGMT_DEBUG) \
{ \
debug_print_prefix(trace_flag, NULL, __FUNCTION__, \
__LINE__); \
PRINT(format, args); \
} \
} while(0)
if (trace_flag & TRACE_MGMT_DEBUG) { \
debug_print_prefix(trace_flag, NULL, \
__func__, __LINE__); \
PRINT(LOG_DEBUG, format, args); \
} \
} while (0)
#define TRACE_BUFFER(message, buff, len) \
do { \
if (trace_flag & TRACE_BUFF) \
{ \
debug_print_prefix(trace_flag, NULL, __FUNCTION__, \
__LINE__); \
PRINT("%s:", message); \
debug_print_buffer(buff, len); \
} \
} while(0)
if (trace_flag & TRACE_BUFF) { \
debug_print_prefix(trace_flag, NULL, \
__func__, __LINE__); \
PRINT(LOG_DEBUG, "%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, NULL, __FUNCTION__, \
__LINE__); \
PRINT("%s:", message); \
debug_print_buffer(buff, len); \
} \
} while(0)
if (trace_flag & (flag)) { \
debug_print_prefix(trace_flag, NULL, \
__func__, __LINE__); \
PRINT(LOG_DEBUG, "%s:", message); \
debug_print_buffer(buff, len); \
} \
} while (0)
#define PRINT_INFO(format, args...) \
do { \
debug_print_prefix(trace_flag, __LOG_PREFIX, __FUNCTION__, \
__LINE__); \
PRINT(format, args); \
} while(0)
debug_print_prefix(trace_flag, __LOG_PREFIX, \
__func__, __LINE__); \
PRINT(LOG_INFO, format, args); \
} while (0)
#define PRINT_WARNING(format, args...) \
do { \
debug_print_prefix(trace_flag, __LOG_PREFIX, __FUNCTION__, \
__LINE__); \
PRINT("***WARNING*** " format, args); \
} while(0)
debug_print_prefix(trace_flag, __LOG_PREFIX, \
__func__, __LINE__); \
PRINT(LOG_WARNING, "***WARNING*** " format, args); \
} while (0)
#define PRINT_ERROR(format, args...) \
do { \
debug_print_prefix(trace_flag, __LOG_PREFIX, __FUNCTION__, \
__LINE__); \
PRINT("***ERROR*** " format, args); \
} while(0)
debug_print_prefix(trace_flag, __LOG_PREFIX, \
__func__, __LINE__); \
PRINT(LOG_ERR, "***ERROR*** " 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)
if (trace_flag & TRACE_ENTRYEXIT) { \
if (trace_flag & TRACE_PID) { \
PRINT(LOG_DEBUG, "[%d]: ENTRY %s", \
gettid(), __func__); \
} else { \
PRINT(LOG_DEBUG, "ENTRY %s", \
__func__); \
} \
} \
} 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)
if (trace_flag & TRACE_ENTRYEXIT) { \
if (trace_flag & TRACE_PID) { \
PRINT(LOG_DEBUG, "[%d]: EXIT %s", \
gettid(), __func__); \
} else { \
PRINT(LOG_DEBUG, "EXIT %s", __func__); \
} \
} \
} 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)
if (trace_flag & TRACE_ENTRYEXIT) { \
if (trace_flag & TRACE_PID) { \
PRINT(LOG_DEBUG, "[%d]: EXIT %s: %ld", \
gettid(), __func__, \
(long)(res)); \
} else { \
PRINT(LOG_DEBUG, "EXIT %s: %ld", \
__func__, (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)
if (trace_flag & TRACE_ENTRYEXIT) { \
if (trace_flag & TRACE_PID) { \
PRINT(LOG_DEBUG, "[%d]: EXIT %s: 0x%lx",\
gettid(), __func__, \
(long)(res)); \
} else { \
PRINT(LOG_DEBUG, "EXIT %s: %lx", \
__func__, (long)(res)); \
} \
} \
} while (0)
#else /* DEBUG */
@@ -271,39 +270,25 @@ do { \
#ifdef LOG_PREFIX
#define PRINT_INFO(format, args...) \
do { \
PRINT("%s: " format, LOG_PREFIX, args); \
} while(0)
PRINT(LOG_INFO, "%s: " format, LOG_PREFIX, args) \
#define PRINT_WARNING(format, args...) \
do { \
PRINT("%s: ***WARNING*** " \
format, LOG_PREFIX, args); \
} while(0)
PRINT(LOG_WARNING, "%s: ***WARNING*** " format, \
LOG_PREFIX, args) \
#define PRINT_ERROR(format, args...) \
do { \
PRINT("%s: ***ERROR*** " \
format, LOG_PREFIX, args); \
} while(0)
PRINT(LOG_ERR, "%s: ***ERROR*** " format, \
LOG_PREFIX, args) \
#else
#define PRINT_INFO(format, args...) \
do { \
PRINT(format, args); \
} while(0)
#define PRINT_INFO(format, args...) PRINT(LOG_INFO, format, args)
#define PRINT_WARNING(format, args...) \
do { \
PRINT("***WARNING*** " format, args); \
} while(0)
PRINT(LOG_WARNING, "***WARNING*** " format, args) \
#define PRINT_ERROR(format, args...) \
do { \
PRINT("***ERROR*** " format, args); \
} while(0)
PRINT(LOG_ERR, "***ERROR*** " format, args) \
#endif /* LOG_PREFIX */

24
usr/include/version.h Normal file
View File

@@ -0,0 +1,24 @@
/*
* version.h
*
* Copyright (C) 2015 SanDisk Corporation
*
* 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 __VERSION_H
#define __VERSION_H
#define VERSION_STR "3.1.0-pre1"
#endif /* __VERSION_H */

104
usr/stpgd/Makefile Normal file
View File

@@ -0,0 +1,104 @@
ifndef PREFIX
PREFIX=/usr/local
endif
SHELL=/bin/bash
SRCS_F = stpgd_main.c debug.c
OBJS_F = $(SRCS_F:.c=.o)
#SRCS_C =
#OBJS_C = $(SRCS_C:.c=.o)
SCST_INC_DIR := $(shell if [ -e "$$PWD/../../scst" ]; \
then echo "$$PWD/../../scst/include"; \
else echo "$(DESTDIR)$(PREFIX)/include/scst"; fi)
DEBUG_INC_DIR := ../include
INSTALL_DIR := $(DESTDIR)$(PREFIX)/sbin
ON_STPG := $(DESTDIR)$(PREFIX)/bin/scst/scst_on_stpg
CFLAGS += -O2 -Wall -Wextra -Wno-unused-parameter -Wstrict-prototypes \
-I$(SCST_INC_DIR) -I$(DEBUG_INC_DIR) -D_GNU_SOURCE -D__USE_FILE_OFFSET64 \
-D__USE_LARGEFILE64
PROGS = stpgd
LIBS = -lpthread
CFLAGS += -DEXTRACHECKS
#CFLAGS += -DTRACING
CFLAGS += -DDEBUG -g -fno-inline -fno-inline-functions
CFLAGS += -W -Wno-unused-parameter
CFLAGS += $(LOCAL_CFLAGS)
#CFLAGS += -DDEBUG_NOMEM
all: $(PROGS)
stpgd: .depend_f $(OBJS_F)
$(CC) $(OBJS_F) $(LIBS) $(LOCAL_LD_FLAGS) -o $@
#cdrom_tgt: .depend_c $(OBJS_C)
# $(CC) $(OBJS_C) $(LIBS) $(LOCAL_LD_FLAGS) -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 700 $(PROGS) $(INSTALL_DIR)
install -m 700 scst_on_stpg $(ON_STPG)
uninstall:
rm -f $(INSTALL_DIR)/$(PROGS)
rm -rf $(INSTALL_DIR)
clean:
rm -f *.o $(PROGS) .depend*
extraclean: clean
rm -f *.orig *.rej
2release:
sed -i.aa s/"^C\?FLAGS += \-DEXTRACHECKS"/"#CFLAGS += \-DEXTRACHECKS"/ Makefile
grep "^#CFLAGS += \-DEXTRACHECKS" Makefile >/dev/null
sed -i.aa s/"^#\?CFLAGS += \-DTRACING"/"CFLAGS += \-DTRACING"/ Makefile
grep "^CFLAGS += \-DTRACING" Makefile >/dev/null
sed -i.aa s/"^C\?FLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/"#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/ Makefile
grep "^#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions" Makefile >/dev/null
rm Makefile.aa
2debug:
sed -i.aa s/"^#\?CFLAGS += \-DEXTRACHECKS"/"CFLAGS += \-DEXTRACHECKS"/ Makefile
grep "^CFLAGS += \-DEXTRACHECKS" Makefile >/dev/null
sed -i.aa s/"^C\?FLAGS += \-DTRACING"/"#CFLAGS += \-DTRACING"/ Makefile
grep "^#CFLAGS += \-DTRACING" Makefile >/dev/null
sed -i.aa s/"^#\?CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/"CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/ Makefile
grep "^CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions" Makefile >/dev/null
rm Makefile.aa
2perf:
sed -i.aa s/"^C\?FLAGS += \-DEXTRACHECKS"/"#CFLAGS += \-DEXTRACHECKS"/ Makefile
grep "^#CFLAGS += \-DEXTRACHECKS" Makefile >/dev/null
sed -i.aa s/"^C\?FLAGS += \-DTRACING"/"#CFLAGS += \-DTRACING"/ Makefile
grep "^#CFLAGS += \-DTRACING" Makefile >/dev/null
sed -i.aa s/"^C\?FLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/"#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/ Makefile
grep "^#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions" Makefile >/dev/null
rm Makefile.aa
release-archive:
../../scripts/generate-release-archive stpgd "$$(sed -n 's/^#define[[:blank:]]VERSION_STR[[:blank:]]*\"\([^\"]*\)\".*/\1/p' stpgd_main.c)"
.PHONY: all install uninstall clean extraclean 2release 2debug 2perf

9
usr/stpgd/README Normal file
View File

@@ -0,0 +1,9 @@
This is stpgd service called by the SCST core upon receiving SET TARGET
PORT GROUPS command via SCST events subsystem. In turn, stpgd calls
external script to actually modify ALUA state as requested by the SET
TARGET PORT GROUPS command via cluster manager and SCST sysfs. You can
find example of such script in scst_on_stpg file.
Reason why such dual stage approach is used is, because there is no way
from inside the kernel to control execution of external programs and
there is no way to write a service calling IOCTLs on shell.

1
usr/stpgd/debug.c Normal file
View File

@@ -0,0 +1 @@
#include "../include/debug.c"

11
usr/stpgd/scst_on_stpg Normal file
View File

@@ -0,0 +1,11 @@
#!/bin/bash
#
# Script invoked by SCST for processing a SCSI SET TARGET PORT GROUPS command
#
# Arguments: SCST_DEVICE_NAME (device name where this STPG command was
# received on), SCST_PREV_ALUA_STATE, SCST_ALUA_STATE,
# SCST_DEVICE_GROUP and SCST_TARGET_GROUP + environmental variables
# with the same names set
#
echo $SCST_ALUA_STATE >/sys/kernel/scst_tgt/device_groups/$SCST_DEVICE_GROUP/target_groups/$SCST_TARGET_GROUP/state

562
usr/stpgd/stpgd_main.c Normal file
View File

@@ -0,0 +1,562 @@
/*
* stpgd_main.c
*/
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <malloc.h>
#include <inttypes.h>
#include <sys/wait.h>
#include <sys/poll.h>
#include <sys/ioctl.h>
#include <stdbool.h>
#include <pthread.h>
#include <unistd.h>
#include <syslog.h>
#include <signal.h>
#include <syslog.h>
#include "version.h"
#include "debug.h"
#include "scst_event.h"
char *app_name;
#define DEFAULT_TRANSITION_TIME 17
#if defined(DEBUG) || defined(TRACING)
#ifdef DEBUG
#define DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MINOR | TRACE_PID | \
TRACE_FUNCTION | TRACE_SPECIAL | TRACE_MGMT | TRACE_MGMT_DEBUG | \
TRACE_TIME)
#define TRACE_SN(args...) TRACE(TRACE_SCSI_SERIALIZING, args)
#else /* DEBUG */
# ifdef TRACING
#define DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MGMT | \
TRACE_TIME | TRACE_SPECIAL)
# else
#define DEFAULT_LOG_FLAGS 0
# endif
#endif /* DEBUG */
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
bool log_daemon = true;
unsigned long trace_flag = DEFAULT_LOG_FLAGS;
#endif /* defined(DEBUG) || defined(TRACING) */
int transition_timeout = DEFAULT_TRANSITION_TIME;
static struct option const long_options[] = {
{"path", required_argument, 0, 'p'},
{"timeout", required_argument, 0, 't'},
{"foreground", no_argument, 0, 'f'},
#if defined(DEBUG) || defined(TRACING)
{"debug", required_argument, 0, 'd'},
#endif
{"version", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0},
};
int stpg_init_report_pipe[2];
char *stpg_path;
static void usage(int status)
{
if (status != 0)
fprintf(stderr, "Try '%s --help' for more information.\n", app_name);
else {
printf("Usage: %s [OPTIONS]\n", app_name);
printf("STPG target daemon\n");
printf(" -f, --foreground make the program run in the foreground\n");
printf(" -p, --path absolute path to the STPG script\n");
printf(" -t, --timeout transition timeout\n");
#if defined(DEBUG) || defined(TRACING)
printf(" -d, --debug=level debug tracing level\n");
#endif
printf(" -h, --help display this help and exit\n");
}
}
static void stpg_handle_tm_received(struct scst_event_user *event_user)
{
/*
* Put code to abort state transition here, if this STPG cmd,
* identified by cmd_to_abort_tag or RESET, requested to be aborted
*/
}
int invoke_stpg(const uint8_t *device_name,
const struct scst_event_stpg_descr *descr, pid_t *out_pid)
{
char *args[7], *env[7];
int res = 0, ret, i;
pid_t c_pid;
args[0] = stpg_path;
args[1] = (char *)device_name;
args[2] = (char *)descr->prev_state;
args[3] = (char *)descr->new_state;
args[4] = (char *)descr->dg_name;
args[5] = (char *)descr->tg_name;
args[6] = NULL;
env[0] = "PATH=/bin:/usr/bin:/sbin:/usr/sbin";
ret = asprintf(&env[1], "SCST_DEVICE_NAME=%s", device_name);
if (ret < 0) {
res = -errno;
PRINT_ERROR("asprintf() failed: %d (%s)", res, strerror(-res));
goto out;
}
ret = asprintf(&env[2], "SCST_PREV_ALUA_STATE=%s", descr->prev_state);
if (ret < 0) {
res = -errno;
PRINT_ERROR("asprintf() failed: %d (%s)", res, strerror(-res));
goto out;
}
ret = asprintf(&env[3], "SCST_ALUA_STATE=%s", descr->new_state);
if (ret < 0) {
res = -errno;
PRINT_ERROR("asprintf() failed: %d (%s)", res, strerror(-res));
goto out;
}
ret = asprintf(&env[4], "SCST_DEVICE_GROUP=%s", descr->dg_name);
if (ret < 0) {
res = -errno;
PRINT_ERROR("asprintf() failed: %d (%s)", res, strerror(-res));
goto out;
}
ret = asprintf(&env[5], "SCST_TARGET_GROUP=%s", descr->tg_name);
if (ret < 0) {
res = -errno;
PRINT_ERROR("asprintf() failed: %d (%s)", res, strerror(-res));
goto out;
}
env[6] = NULL;
PRINT_INFO("Invoking script %s with parameters: %s %s %s %s %s and environment: "
"%s %s %s %s %s", stpg_path, args[1], args[2], args[3],
args[4], args[5], env[1], env[2], env[3], env[4], env[5]);
c_pid = fork();
if (c_pid == 0) {
ret = setpgid(getpid(), getpid());
if (ret < 0) {
res = -errno;
PRINT_ERROR("setgid failed %d (%s)", ret, strerror(-ret));
}
TRACE_DBG("pgid %d (pid %d)", getpgid(getpid()), getpid());
ret = execve(stpg_path, args, env);
if (ret < 0) {
res = -errno;
PRINT_ERROR("EXEC failed %d (%s)", ret, strerror(-ret));
}
exit(0);
} else if (c_pid < 0) {
res = -errno;
PRINT_ERROR("fork() failed: %d (%s)", res, strerror(-res));
}
*out_pid = c_pid;
for (i = 1; i < (signed)ARRAY_SIZE(env); i++)
free(env[i]);
out:
return res;
}
/* Returns 0, if the pid is still running, >0 if it was exited or <0 error code */
int wait_until_finished(pid_t pid, unsigned long deadline, int *status, int child)
{
int res;
time_t start, end;
double elapsed;
TRACE_ENTRY();
time(&start);
do {
res = waitpid(pid, status, WNOHANG);
if (res != 0) {
if (res < 0) {
res = -errno;
PRINT_ERROR("Waitpid for pid %d (child %d) "
"failed: %d (%s)", pid, child,
errno, strerror(errno));
}
break;
}
sleep(0.1);
time(&end);
elapsed = difftime(end, start);
} while (elapsed < deadline);
TRACE_EXIT_RES(res);
return res;
}
int handle_stpg_received(struct scst_event_user *event_user)
{
const struct scst_event_stpg_payload *p = (struct scst_event_stpg_payload *)event_user->out_event.payload;
int num, k;
int res = 0;
pid_t pids[p->stpg_descriptors_cnt];
TRACE_DBG("device name %s, stpg_descriptors_cnt %d", p->device_name,
p->stpg_descriptors_cnt);
for (num = 0; num < p->stpg_descriptors_cnt; num++) {
res = invoke_stpg(p->device_name, &p->stpg_descriptors[num], &pids[num]);
TRACE_DBG("num %d, res %d, pid %d", num, res, pids[num]);
if (res != 0)
break;
}
TRACE_DBG("num %d", num);
for (k = 0; k < num; k++) {
int status = 0, rc;
TRACE_DBG("k %d, pid %d", k, pids[k]);
rc = wait_until_finished(pids[k], transition_timeout, &status, k);
TRACE_DBG("rc %d, status %d", rc, WEXITSTATUS(status));
if (rc > 0) {
if (res == 0)
res = WEXITSTATUS(status);
continue;
} else if (rc < 0) {
if (res == 0)
res = rc;
continue;
}
PRINT_WARNING("on_stpg %d (pid %d) did not finish on time - "
"sending SIGTERM", k, pids[k]);
if (res == 0)
res = -ETIMEDOUT;
rc = killpg(pids[k], SIGTERM);
if (rc < 0)
PRINT_ERROR("Failed to send SIGTERM to child %d (pid %d): %d/%s",
k, pids[k], errno, strerror(errno));
rc = wait_until_finished(pids[k], 1, &status, k);
if (rc != 0)
continue;
while (1) {
PRINT_WARNING("on_stpg %d (pid %d) did not finish on time - "
"sending SIGKILL", k, pids[k]);
rc = killpg(pids[k], SIGKILL);
if (rc < 0) {
PRINT_ERROR("Failed to send SIGKILL to child %d "
"(pid %d): %d/%s", k, pids[k], errno,
strerror(errno));
break;
}
rc = wait_until_finished(pids[k], 1, &status, k);
if (rc != 0)
break;
};
}
TRACE_EXIT_RES(res);
return res;
}
static int stpg_event_loop(void)
{
int res = 0, status;
int event_fd;
uint8_t event_user_buf[1024*1024];
pid_t c_pid = 0;
struct pollfd pl;
struct scst_event_user *event_user =
(struct scst_event_user *)event_user_buf;
struct scst_event e1;
bool first_error = true;
event_fd = open(SCST_EVENT_DEV, O_RDWR);
if (event_fd < 0) {
res = -errno;
PRINT_ERROR("Unable to open SCST event device %s (%s)",
SCST_EVENT_DEV, strerror(-res));
goto out;
}
close(stpg_init_report_pipe[0]);
if (log_daemon)
res = write(stpg_init_report_pipe[1], &res, sizeof(res));
close(stpg_init_report_pipe[1]);
memset(&pl, 0, sizeof(pl));
pl.fd = event_fd;
pl.events = POLLIN;
memset(&e1, 0, sizeof(e1));
e1.event_code = SCST_EVENT_STPG_USER_INVOKE;
strncpy(e1.issuer_name, SCST_EVENT_SCST_CORE_ISSUER,
sizeof(e1.issuer_name));
e1.issuer_name[sizeof(e1.issuer_name)-1] = '\0';
PRINT_INFO("Setting allowed event code %d, issuer_name %s",
e1.event_code, e1.issuer_name);
res = ioctl(event_fd, SCST_EVENT_ALLOW_EVENT, &e1);
if (res != 0) {
res = -errno;
PRINT_ERROR("SCST_EVENT_ALLOW_EVENT failed: %d (%s)",
res, strerror(-res));
goto out;
}
e1.event_code = SCST_EVENT_TM_FN_RECEIVED;
strncpy(e1.issuer_name, SCST_EVENT_SCST_CORE_ISSUER,
sizeof(e1.issuer_name));
e1.issuer_name[sizeof(e1.issuer_name)-1] = '\0';
PRINT_INFO("Setting allowed event code %d, issuer_name %s",
e1.event_code, e1.issuer_name);
res = ioctl(event_fd, SCST_EVENT_ALLOW_EVENT, &e1);
if (res != 0) {
res = -errno;
PRINT_ERROR("SCST_EVENT_ALLOW_EVENT failed: %d (%s)",
res, strerror(-res));
goto out;
}
while (1) {
memset(event_user_buf, 0, sizeof(event_user_buf));
event_user->max_event_size = sizeof(event_user_buf);
res = ioctl(event_fd, SCST_EVENT_GET_NEXT_EVENT, event_user);
if (res != 0) {
res = -errno;
switch (-res) {
case ESRCH:
case EBUSY:
TRACE_MGMT_DBG("SCST_EVENT_GET_NEXT_EVENT "
"returned %d (%s)", res, strerror(res));
/* go through */
case EINTR:
continue;
case EAGAIN:
TRACE_DBG("SCST_EVENT_GET_NEXT_EVENT, "
"returned EAGAIN (%d)", -res);
continue;
default:
PRINT_ERROR("SCST_EVENT_GET_NEXT_EVENT "
"failed: %d (%s)", res, strerror(-res));
if (!first_error)
goto out;
first_error = false;
continue;
}
first_error = true;
again_poll:
res = poll(&pl, 1, c_pid > 0 ? 1 : 0);
if (res > 0)
continue;
else if (res == 0)
goto again_poll;
else {
res = -errno;
switch (res) {
case ESRCH:
case EBUSY:
case EAGAIN:
TRACE_MGMT_DBG("poll() returned %d "
"(%s)", res, strerror(-res));
case EINTR:
goto again_poll;
default:
PRINT_ERROR("poll() failed: %d (%s)",
res, strerror(-res));
goto again_poll;
}
}
}
first_error = true;
#ifdef DEBUG
PRINT_INFO("event_code %d, issuer_name %s",
event_user->out_event.event_code,
event_user->out_event.issuer_name);
#endif
if (event_user->out_event.payload_len != 0)
TRACE_BUFFER("payload", event_user->out_event.payload,
event_user->out_event.payload_len);
if (event_user->out_event.event_code == SCST_EVENT_STPG_USER_INVOKE) {
c_pid = fork();
if (c_pid == -1)
PRINT_ERROR("Failed to fork: %d", c_pid);
else if (c_pid == 0) {
struct scst_event_notify_done d;
signal(SIGCHLD, SIG_DFL);
status = handle_stpg_received(event_user);
memset(&d, 0, sizeof(d));
d.event_id = event_user->out_event.event_id;
d.status = status;
res = ioctl(event_fd, SCST_EVENT_NOTIFY_DONE, &d);
if (res != 0) {
res = -errno;
PRINT_ERROR("SCST_EVENT_NOTIFY_DONE "
"failed: %s (res %d)",
strerror(-res), res);
} else
PRINT_INFO("STPG event completed with status %d", status);
exit(res);
}
} else if (event_user->out_event.event_code == SCST_EVENT_TM_FN_RECEIVED)
stpg_handle_tm_received(event_user);
else
PRINT_ERROR("Unknown event %d received", event_user->out_event.event_code);
}
out:
return res;
}
void sig_chld(int signal)
{
/* Check just in case */
if (signal == SIGCHLD) {
TRACE_DBG("Cleanup zombie (pid %d)", getpid());
wait(NULL);
}
}
int main(int argc, char **argv)
{
int res = 0, ch, longindex;
pid_t pid;
struct sigaction sa;
setlinebuf(stdout);
openlog(argv[0], LOG_PID, LOG_USER);
res = pipe(stpg_init_report_pipe);
if (res == -1) {
res = -errno;
PRINT_ERROR("Pipe failed: %d (%s)", res, strerror(-res));
goto out;
}
sa.sa_handler = &sig_chld;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
if (sigaction(SIGCHLD, &sa, 0) == -1) {
PRINT_ERROR("sigaction() failed: %d/%s", errno, strerror(errno));
exit(1);
}
/*
* Otherwise we could die in some later write() during the event_loop()
* instead of getting EPIPE!
*/
signal(SIGPIPE, SIG_IGN);
res = debug_init();
if (res != 0)
goto out;
app_name = argv[0];
while ((ch = getopt_long(argc, argv, "+d:fp:t:hv",
long_options, &longindex)) >= 0) {
switch (ch) {
case 'p':
stpg_path = optarg;
break;
#if defined(DEBUG) || defined(TRACING)
case 'd':
trace_flag = strtol(optarg, (char **)NULL, 0);
break;
#endif
case 'v':
printf("%s version %s\n", app_name, VERSION_STR);
goto out_done;
case 'f':
log_daemon = false;
break;
case 't':
transition_timeout = strtol(optarg, (char **)NULL, 0);
if (transition_timeout < 0) {
printf("Invalid timeout %d\n", transition_timeout);
res = -EINVAL;
goto out_done;
}
break;
case 'h':
usage(0);
goto out_done;
default:
goto out_usage;
}
}
if (!stpg_path)
stpg_path = "/usr/local/bin/scst/scst_on_stpg";
if (access(stpg_path, X_OK) == -1) {
PRINT_ERROR("Script file \" %s \"does not exist or not "
"executable", stpg_path);
res = -1;
goto out_done;
}
#ifdef DEBUG
PRINT_INFO("trace_flag %lx", trace_flag);
#endif
if (log_daemon) {
trace_flag &= ~TRACE_TIME;
trace_flag &= ~TRACE_PID;
pid = fork();
if (pid < 0) {
PRINT_ERROR("starting daemon failed(%d)", pid);
res = pid;
goto out_done;
} else if (pid) {
int res1 = -1;
close(stpg_init_report_pipe[1]);
if ((unsigned)read(stpg_init_report_pipe[0], &res1, sizeof(res1)) < sizeof(res1)) {
res = -1;
goto out_done;
} else {
res = res1;
goto out_done;
}
}
close(0);
open("/dev/null", O_RDWR);
dup2(0, 1);
dup2(0, 2);
setsid();
}
res = stpg_event_loop();
out_done:
debug_done();
out:
closelog();
return res;
out_usage:
usage(1);
goto out_done;
}