Initial commit
Signed-off-by: Robert Baldyga <robert.baldyga@intel.com>
This commit is contained in:
commit
94e8ca09e0
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "ocf"]
|
||||
path = ocf
|
||||
url = git@github.com:Open-CAS/ocf.git
|
15
Makefile
Normal file
15
Makefile
Normal file
@ -0,0 +1,15 @@
|
||||
#
|
||||
# Copyright(c) 2012-2019 Intel Corporation
|
||||
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
#
|
||||
|
||||
default: all
|
||||
|
||||
DIRS:=modules casadm utils
|
||||
|
||||
.PHONY: default all clean distclean $(DIRS)
|
||||
|
||||
all $(MAKECMDGOALS): $(DIRS)
|
||||
|
||||
$(DIRS):
|
||||
cd $@ && $(MAKE) $(MAKECMDGOALS)
|
167
casadm/Makefile
Normal file
167
casadm/Makefile
Normal file
@ -0,0 +1,167 @@
|
||||
#
|
||||
# Copyright(c) 2012-2019 Intel Corporation
|
||||
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
#
|
||||
|
||||
PWD:=$(shell pwd)
|
||||
MODULESDIR:=$(PWD)/../modules
|
||||
UTILS_DIR:=$(PWD)/../utils
|
||||
BINARY_PATH = /sbin
|
||||
|
||||
VERSION_FILE := $(MODULESDIR)/CAS_VERSION
|
||||
|
||||
#
|
||||
# Section below enables creating build with experimental features
|
||||
#
|
||||
ifeq ($(CAS_EXT_EXP),1)
|
||||
DEFINES = WI_AVAILABLE
|
||||
endif
|
||||
|
||||
#
|
||||
# Add defines for version
|
||||
#
|
||||
-include $(VERSION_FILE)
|
||||
DEFINES += CAS_VERSION_MAIN=$(CAS_VERSION_MAIN)
|
||||
DEFINES += CAS_VERSION_MAJOR=$(CAS_VERSION_MAJOR)
|
||||
DEFINES += CAS_VERSION_MINOR=$(CAS_VERSION_MINOR)
|
||||
DEFINES += CAS_BUILD_NO=\"$(CAS_BUILD_NO)\"
|
||||
|
||||
#
|
||||
# Additional git version
|
||||
#
|
||||
ifneq ($(strip $(CAS_BUILD_FLAG)),)
|
||||
DEFINES += CAS_BUILD_FLAG=\"$(CAS_BUILD_FLAG)\"
|
||||
endif
|
||||
|
||||
#
|
||||
# Include directories
|
||||
#
|
||||
INCLUDES = .
|
||||
INCLUDES += $(MODULESDIR)/include
|
||||
|
||||
OBJDIR = .obj/
|
||||
TARGET = casadm
|
||||
TARGETS = $(TARGET)
|
||||
|
||||
#
|
||||
# Source to be complied
|
||||
#
|
||||
|
||||
OBJS = cas_lib.o
|
||||
OBJS += cas_main.o
|
||||
OBJS += argp.o
|
||||
OBJS += statistics_view_csv.o
|
||||
OBJS += cas_lib_utils.o
|
||||
OBJS += statistics_model.o
|
||||
OBJS += table.o
|
||||
OBJS += psort.o
|
||||
OBJS += statistics_view_text.o
|
||||
OBJS += intvector.o
|
||||
OBJS += statistics_view.o
|
||||
OBJS += statistics_view_raw_csv.o
|
||||
OBJS += csvparse.o
|
||||
OBJS += extended_err_msg.o
|
||||
OBJS += upgrade.o
|
||||
OBJS += safeclib/memmove_s.o
|
||||
OBJS += safeclib/memcpy_s.o
|
||||
OBJS += safeclib/memset_s.o
|
||||
OBJS += safeclib/strncpy_s.o
|
||||
OBJS += safeclib/strtok_s.o
|
||||
OBJS += safeclib/safe_str_constraint.o
|
||||
OBJS += safeclib/ignore_handler_s.o
|
||||
OBJS += safeclib/safe_mem_constraint.o
|
||||
OBJS += safeclib/mem_primitives_lib.o
|
||||
OBJS += safeclib/strnlen_s.o
|
||||
|
||||
#
|
||||
# Flags for C compilation
|
||||
#
|
||||
CFLAGS = $(patsubst %,-I%,$(INCLUDES))
|
||||
CFLAGS += $(patsubst %,-D%,$(DEFINES))
|
||||
ifdef DEBUG
|
||||
CFLAGS += -O0 -g
|
||||
else
|
||||
CFLAGS += -O2 -D_FORTIFY_SOURCE=2
|
||||
endif
|
||||
CFLAGS += -Wall -z relro -z now -fstack-protector -fPIC -Wformat -Wformat-security -fno-strict-aliasing
|
||||
|
||||
#
|
||||
# Flags for linking
|
||||
#
|
||||
LDFLAGS = -z noexecstack -z relro -z now -pie -pthread
|
||||
#
|
||||
# Targets
|
||||
#
|
||||
|
||||
all: sync
|
||||
$(MAKE) build
|
||||
|
||||
build: $(VERSION_FILE) $(TARGETS)
|
||||
|
||||
sync:
|
||||
@cd $(MODULESDIR) && $(MAKE) sync
|
||||
|
||||
#
|
||||
# Include dependencies file
|
||||
#
|
||||
$(TARGET): $(TARGET).a
|
||||
@echo " LD " $@
|
||||
@$(CC) $(CFLAGS) $(LDFLAGS) -o $(TARGET) $<
|
||||
|
||||
$(TARGET).a: $(patsubst %,$(OBJDIR)%,$(OBJS))
|
||||
@echo " AR " $@
|
||||
@ar rcs $@ $^
|
||||
@echo " AR " libcas.a
|
||||
@cp -f $@ libcas.a
|
||||
@ar d libcas.a $(OBJDIR)argp.o $(OBJDIR)cas_main.c
|
||||
|
||||
#
|
||||
# Generic target for C file
|
||||
#
|
||||
$(OBJDIR)%.o: %.c
|
||||
@echo " CC " $<
|
||||
@mkdir -p $(dir $@)
|
||||
ifeq ($(strip $(CAS_VERSION_MAIN)),)
|
||||
$(error "No version file")
|
||||
endif
|
||||
@$(CC) -c $(CFLAGS) -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -o "$@" "$<"
|
||||
|
||||
$(VERSION_FILE):
|
||||
@echo " VERSION " $@
|
||||
@cd $(MODULESDIR) && ./CAS_VERSION_GEN
|
||||
|
||||
clean:
|
||||
@echo " CLEAN "
|
||||
@rm -f *.a $(TARGETS)
|
||||
@rm -f $(shell find -name \*.d) $(shell find -name \*.o)
|
||||
|
||||
distclean: clean
|
||||
@rm -f $(VERSION_FILE)
|
||||
|
||||
install:
|
||||
@echo "Installing casadm"
|
||||
@install -m 755 $(TARGET) $(BINARY_PATH)/$(TARGET)
|
||||
@install -m 644 $(UTILS_DIR)/$(TARGET).8 /usr/share/man/man8/$(TARGET).8
|
||||
|
||||
@install -m 755 -d /etc/opencas
|
||||
@install -m 644 $(UTILS_DIR)/opencas.conf /etc/opencas/opencas.conf
|
||||
@install -m 444 $(UTILS_DIR)/ioclass-config.csv /etc/opencas/ioclass-config.csv
|
||||
@install -m 444 $(UTILS_DIR)/ext3-config.csv /etc/opencas/ext3-config.csv
|
||||
@install -m 444 $(UTILS_DIR)/ext4-config.csv /etc/opencas/ext4-config.csv
|
||||
|
||||
@install -m 644 $(UTILS_DIR)/opencas.conf.5 /usr/share/man/man5/opencas.conf.5
|
||||
|
||||
uninstall:
|
||||
@echo "Uninstalling casadm"
|
||||
@rm $(BINARY_PATH)/$(TARGET)
|
||||
@rm /usr/share/man/man8/$(TARGET).8
|
||||
|
||||
@rm /etc/opencas/opencas.conf
|
||||
@rm /etc/opencas/ioclass-config.csv
|
||||
@rm /etc/opencas/ext3-config.csv
|
||||
@rm /etc/opencas/ext4-config.csv
|
||||
@rm -rf /etc/opencas
|
||||
|
||||
@rm /usr/share/man/man5/opencas.conf.5
|
||||
|
||||
.PHONY: clean distclean all sync build install uninstall
|
803
casadm/argp.c
Normal file
803
casadm/argp.c
Normal file
@ -0,0 +1,803 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "argp.h"
|
||||
#include "cas_lib.h"
|
||||
#include "cas_lib_utils.h"
|
||||
#include <sys/timeb.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define PADDING " "
|
||||
#define MAX_OPT_HELP_LEN 30
|
||||
|
||||
extern cas_printf_t cas_printf;
|
||||
|
||||
static int is_su_requied(const cli_command* commands, int cmd)
|
||||
{
|
||||
return commands[cmd].flags & CLI_SU_REQUIRED;
|
||||
}
|
||||
|
||||
static int is_command_hidden(const cli_command* commands, int cmd)
|
||||
{
|
||||
return commands[cmd].flags & CLI_COMMAND_HIDDEN;
|
||||
}
|
||||
|
||||
static void print_short_usage(const app *app_values)
|
||||
{
|
||||
cas_printf(LOG_INFO, "Usage: %s %s\n", app_values->name, app_values->info);
|
||||
}
|
||||
|
||||
static void print_info(const app *app_values)
|
||||
{
|
||||
cas_printf(LOG_INFO, "Try `%s --help | -H' for more information.\n", app_values->name);
|
||||
}
|
||||
|
||||
char *get_short_name_string(const char short_name, char *buf)
|
||||
{
|
||||
if (short_name) {
|
||||
snprintf(buf, 3, "-%c", short_name);
|
||||
} else {
|
||||
buf[0] = 0;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *command_name_with_slash(char *buf, size_t buf_size, char short_name, char *long_name) {
|
||||
if (short_name) {
|
||||
snprintf(buf, buf_size, "-%c/--%s", short_name, long_name);
|
||||
} else {
|
||||
snprintf(buf, buf_size, "--%s", long_name);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *command_name_in_brackets(char *buf, size_t buf_size, char short_name, char *long_name) {
|
||||
if (short_name) {
|
||||
snprintf(buf, buf_size, "--%s (-%c)", long_name, short_name);
|
||||
} else {
|
||||
snprintf(buf, buf_size, "--%s", long_name);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
void print_options_usage(cli_option* options, const char *separator,
|
||||
int (*view)(cli_option* options, int flag), int flag)
|
||||
{
|
||||
int print_separator = 0;
|
||||
int i;
|
||||
|
||||
if (NULL == options) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; options[i].long_name != NULL; ++i) {
|
||||
if (0 == view(&options[i], flag)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (print_separator) {
|
||||
/* Separator */
|
||||
cas_printf(LOG_INFO, "%s", separator);
|
||||
}
|
||||
print_separator = 1;
|
||||
|
||||
/* Long option name */
|
||||
cas_printf(LOG_INFO, "--%s", options[i].long_name);
|
||||
|
||||
/* Parameter */
|
||||
if (options[i].arg != NULL) {
|
||||
cas_printf(LOG_INFO, " <%s>",
|
||||
options[i].arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void print_command_header(const app *app_values, const cli_command *cmd)
|
||||
{
|
||||
cas_printf(LOG_INFO, "%s%s\n\n", PADDING,
|
||||
cmd->long_desc != NULL ? cmd->long_desc : cmd->desc);
|
||||
}
|
||||
|
||||
void print_list_options(cli_option* options, int flag,
|
||||
int (*view)(cli_option* options, int flag))
|
||||
{
|
||||
char buffer[2048];
|
||||
|
||||
for (; options->long_name != NULL; options++) {
|
||||
char *desc = options->desc;
|
||||
char short_name[3];
|
||||
|
||||
if (0 == view(options, flag)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((options->flags & CLI_OPTION_RANGE_INT)
|
||||
|| (options->flags & CLI_OPTION_DEFAULT_INT)) {
|
||||
desc = buffer;
|
||||
|
||||
if ((options->flags & CLI_OPTION_RANGE_INT)
|
||||
&& (options->flags
|
||||
& CLI_OPTION_DEFAULT_INT)) {
|
||||
snprintf(buffer, sizeof(buffer), options->desc,
|
||||
options->min_value,
|
||||
options->max_value,
|
||||
options->default_value);
|
||||
} else if (options->flags & CLI_OPTION_DEFAULT_INT) {
|
||||
snprintf(buffer, sizeof(buffer), options->desc,
|
||||
options->default_value);
|
||||
} else if (options->flags & CLI_OPTION_RANGE_INT) {
|
||||
snprintf(buffer, sizeof(buffer), options->desc,
|
||||
options->min_value,
|
||||
options->max_value);
|
||||
}
|
||||
}
|
||||
|
||||
get_short_name_string(options->short_name, short_name);
|
||||
if (options->arg != NULL) {
|
||||
char buf[MAX_OPT_HELP_LEN];
|
||||
if (options->flags & CLI_OPTION_OPTIONAL_ARG) {
|
||||
snprintf(buf, MAX_OPT_HELP_LEN, "--%s [<%s>]",
|
||||
options->long_name, options->arg);
|
||||
} else {
|
||||
snprintf(buf, MAX_OPT_HELP_LEN, "--%s <%s>",
|
||||
options->long_name, options->arg);
|
||||
}
|
||||
|
||||
cas_printf(LOG_INFO, "%s%-4s%-32s%s\n", PADDING,
|
||||
short_name, buf, desc);
|
||||
} else {
|
||||
cas_printf(LOG_INFO, "%s%-4s--%-30s%s\n", PADDING,
|
||||
short_name, options->long_name, desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void print_options_help(cli_option *options)
|
||||
{
|
||||
char buffer[2048];
|
||||
int i;
|
||||
|
||||
for (i = 0; options[i].long_name != NULL; ++i) {
|
||||
char *desc = options[i].desc;
|
||||
char short_name[3];
|
||||
if (options[i].flags & CLI_OPTION_HIDDEN) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((options[i].flags & CLI_OPTION_RANGE_INT)
|
||||
|| (options[i].flags & CLI_OPTION_DEFAULT_INT) ) {
|
||||
desc = buffer;
|
||||
|
||||
if ((options[i].flags & CLI_OPTION_RANGE_INT)
|
||||
&& (options[i].flags & CLI_OPTION_DEFAULT_INT) ) {
|
||||
snprintf(buffer, sizeof(buffer), options[i].desc,
|
||||
options[i].min_value,
|
||||
options[i].max_value,
|
||||
options[i].default_value);
|
||||
} else if (options[i].flags & CLI_OPTION_DEFAULT_INT) {
|
||||
snprintf(buffer, sizeof(buffer), options[i].desc,
|
||||
options[i].default_value);
|
||||
} else if (options[i].flags & CLI_OPTION_RANGE_INT) {
|
||||
snprintf(buffer, sizeof(buffer), options[i].desc,
|
||||
options[i].min_value,
|
||||
options[i].max_value);
|
||||
}
|
||||
}
|
||||
get_short_name_string(options[i].short_name, short_name);
|
||||
if (options[i].arg != NULL) {
|
||||
char buf[MAX_OPT_HELP_LEN];
|
||||
if (options[i].flags & CLI_OPTION_OPTIONAL_ARG) {
|
||||
snprintf(buf, MAX_OPT_HELP_LEN, "--%s [<%s>]",
|
||||
options[i].long_name,
|
||||
options[i].arg);
|
||||
} else {
|
||||
snprintf(buf, MAX_OPT_HELP_LEN, "--%s <%s>",
|
||||
options[i].long_name,
|
||||
options[i].arg);
|
||||
}
|
||||
|
||||
cas_printf(LOG_INFO, "%s%-4s%-32s%s\n", PADDING,
|
||||
short_name, buf, desc);
|
||||
} else {
|
||||
cas_printf(LOG_INFO, "%s%-4s--%-30s%s\n", PADDING,
|
||||
short_name, options[i].long_name,
|
||||
desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void print_namespace_help(app *app_values, cli_command *cmd)
|
||||
{
|
||||
char command_name[MAX_STR_LEN];
|
||||
char option_name[MAX_STR_LEN];
|
||||
cli_namespace *ns = cmd->namespace;
|
||||
int i;
|
||||
|
||||
cas_printf(LOG_INFO, "Usage: %s --%s --%s <NAME>\n\n", app_values->name,
|
||||
cmd->name, ns->long_name);
|
||||
|
||||
print_command_header(app_values, cmd);
|
||||
|
||||
command_name_in_brackets(command_name, MAX_STR_LEN, cmd->short_name, cmd->name);
|
||||
command_name_in_brackets(option_name, MAX_STR_LEN, ns->short_name, ns->long_name);
|
||||
|
||||
|
||||
cas_printf(LOG_INFO, "Valid values of NAME are:\n");
|
||||
for (i = 0; ns->entries[i].name; ++i)
|
||||
cas_printf(LOG_INFO, "%s%s - %s\n", PADDING, ns->entries[i].name, ns->entries[i].desc);
|
||||
|
||||
cas_printf(LOG_INFO, "\n");
|
||||
|
||||
for (i = 0; ns->entries[i].name; ++i) {
|
||||
cas_printf(LOG_INFO, "Options that are valid with %s %s %s are:\n",
|
||||
command_name, option_name, ns->entries[i].name);
|
||||
print_options_help(ns->entries[i].options);
|
||||
if (ns->entries[i + 1].name)
|
||||
cas_printf(LOG_INFO, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void print_command_help(app *app_values, cli_command *cmd)
|
||||
{
|
||||
int all_mandatory = 1;
|
||||
int all_hidden = 1;
|
||||
int i;
|
||||
|
||||
if (cmd->help) {
|
||||
(cmd->help)(app_values, cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd->namespace) {
|
||||
print_namespace_help(app_values, cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
cas_printf(LOG_INFO, "Usage: %s --%s", app_values->name, cmd->name);
|
||||
|
||||
if (cmd->options != NULL) {
|
||||
for (i = 0; cmd->options[i].long_name != NULL; ++i) {
|
||||
if (cmd->options[i].flags & CLI_OPTION_HIDDEN) {
|
||||
continue;
|
||||
}
|
||||
|
||||
all_hidden = 0;
|
||||
|
||||
if (cmd->options[i].flags & CLI_OPTION_REQUIRED) {
|
||||
cas_printf(LOG_INFO, " --%s", cmd->options[i].long_name);
|
||||
if (cmd->options[i].arg != NULL) {
|
||||
if (cmd->options[i].flags & CLI_OPTION_OPTIONAL_ARG) {
|
||||
cas_printf(LOG_INFO, " [<%s>]", cmd->options[i].arg);
|
||||
} else {
|
||||
cas_printf(LOG_INFO, " <%s>", cmd->options[i].arg);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
all_mandatory = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!all_mandatory) {
|
||||
cas_printf(LOG_INFO, " [option...]");
|
||||
}
|
||||
}
|
||||
cas_printf(LOG_INFO, "\n\n");
|
||||
|
||||
print_command_header(app_values, cmd);
|
||||
|
||||
if (cmd->options && !all_hidden) {
|
||||
char option_name[MAX_STR_LEN];
|
||||
command_name_in_brackets(option_name, MAX_STR_LEN, cmd->short_name, cmd->name);
|
||||
cas_printf(LOG_INFO, "Options that are valid with %s are:\n", option_name);
|
||||
print_options_help(cmd->options);
|
||||
}
|
||||
}
|
||||
|
||||
void print_help(const app *app_values, const cli_command *commands)
|
||||
{
|
||||
int i;
|
||||
|
||||
cas_printf(LOG_INFO, "%s\n\n", app_values->title);
|
||||
print_short_usage(app_values);
|
||||
cas_printf(LOG_INFO, "\nAvailable commands:\n");
|
||||
|
||||
for (i = 0;; ++i) {
|
||||
char short_name[3];
|
||||
|
||||
if (commands[i].name == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_command_hidden(commands, i))
|
||||
continue;
|
||||
|
||||
get_short_name_string(commands[i].short_name, short_name);
|
||||
|
||||
cas_printf(LOG_INFO, "%s%-4s--%-25s%s\n", PADDING, short_name,
|
||||
commands[i].name, commands[i].desc);
|
||||
}
|
||||
|
||||
cas_printf(LOG_INFO, "\nFor detailed help on the above commands use --help after the command.\n"
|
||||
"e.g.\n%s%s --%s --help\n",
|
||||
PADDING, app_values->name, commands[0].name);
|
||||
|
||||
if (app_values->man != NULL) {
|
||||
cas_printf(LOG_INFO,
|
||||
"For more information, please refer to manual, Admin Guide (man %s)\n"
|
||||
"or go to support page <http://www.intel.com/support/go/cas>.\n",
|
||||
app_values->man);
|
||||
} else {
|
||||
cas_printf(LOG_INFO,
|
||||
"For more information, please refer to manual, Admin Guide\n"
|
||||
"or go to support page <http://www.intel.com/support/go/cas>.\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int args_is_unrecognized(const char *cmd)
|
||||
{
|
||||
if (strempty(cmd)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ('-' == cmd[0]) {
|
||||
char c = cmd[1];
|
||||
|
||||
/* Check if short option (command) is proper */
|
||||
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
|
||||
if ('\0' == cmd[2]) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ('-' == cmd[1]) {
|
||||
char c = cmd[2];
|
||||
/* Long option (command), check if it is valid */
|
||||
|
||||
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int args_is(const char *in, const char *arg, const char c)
|
||||
{
|
||||
if (strempty(in)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ('-' == in[0]) {
|
||||
if (0 != c && c == in[1]) {
|
||||
if ('\0' == in[2]) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ('-' == in[1]) {
|
||||
/* Long option */
|
||||
if (0 == strncmp(&(in[2]), arg, MAX_STR_LEN)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_help(const char* cmd)
|
||||
{
|
||||
return args_is(cmd, "help", 'H');
|
||||
}
|
||||
|
||||
static int get_help_position(int argc, const char **argv)
|
||||
{
|
||||
int i;
|
||||
for (i = 2; i < argc; i++) {
|
||||
if (is_help(argv[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int get_option(const cli_option *options, const char* opt)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; options[i].long_name; ++i) {
|
||||
if (args_is(opt, options[i].long_name, options[i].short_name)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* log command as it was entered in CLI
|
||||
*/
|
||||
void log_command(int argc, const char **argv, int result, long long int timespan)
|
||||
{
|
||||
const int def_cmd_buf_len = 100;
|
||||
int cmd_buf_len = def_cmd_buf_len;
|
||||
int cmd_len = 0;
|
||||
int i = 0;
|
||||
char *command = malloc(cmd_buf_len);
|
||||
|
||||
if (!command) {
|
||||
cas_printf(LOG_ERR, "Memory allocation failed for logging.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0 ; i != argc ; ++i) {
|
||||
int tok_len = strnlen_s(argv[i], MAX_STR_LEN);
|
||||
/* if reconstructed command width is longer than current
|
||||
* cmd_buf_len (length of command buffer), than resize it
|
||||
* to make it twice as big.
|
||||
*/
|
||||
if (tok_len + 1 + cmd_len > cmd_buf_len) {
|
||||
cmd_buf_len = (tok_len + 1 + cmd_buf_len) * 2;
|
||||
char *tmp = realloc(command, cmd_buf_len);
|
||||
/* if reallocation failed, cancel logging */
|
||||
if (!tmp) {
|
||||
cas_printf(LOG_ERR,
|
||||
"Memory allocation failed for logging.");
|
||||
free(command);
|
||||
return;
|
||||
}
|
||||
command = tmp;
|
||||
}
|
||||
/* append additional token to a string */
|
||||
memcpy_s(command + cmd_len, cmd_buf_len - cmd_len,
|
||||
argv[i], tok_len);
|
||||
cmd_len += tok_len;
|
||||
/* either a space or a null terminator */
|
||||
command[cmd_len] = (i == argc - 1) ? 0 : ' ';
|
||||
cmd_len++;
|
||||
}
|
||||
|
||||
caslog(LOG_DEBUG, "Casadm invoked with: \"%s\". "
|
||||
"Exit status is %d (%s). Command took %lld.%02lld s.",
|
||||
command, result, result? "failure" : "success",
|
||||
timespan / 1000, (timespan % 1000) / 10);
|
||||
free(command);
|
||||
}
|
||||
|
||||
/**
|
||||
* run command. Additionally log its execution and report any errors if
|
||||
* they've happened
|
||||
*/
|
||||
int run_command(cli_command *commands, int cmd, int argc, const char **argv)
|
||||
{
|
||||
int result;
|
||||
const char *syslog_path = "/var/log/messages";
|
||||
/* time buffer and stat buffer after running command */
|
||||
struct timeb t0;
|
||||
FILE *messages_f;
|
||||
/* time buffer and stat buffer after running command */
|
||||
struct timeb t1;
|
||||
long long int timespan;
|
||||
|
||||
/* collect time */
|
||||
ftime(&t0);
|
||||
/* collect stat buffer for syslog */
|
||||
messages_f = fopen(syslog_path, "r");
|
||||
if (messages_f) {
|
||||
fseek(messages_f, 0, SEEK_END);
|
||||
/* if opening file failed, don't stop command execution.
|
||||
* - just omit checking for /var/log/messages at the end
|
||||
*/
|
||||
} else {
|
||||
/* ubuntu case*/
|
||||
syslog_path = "/var/log/syslog";
|
||||
messages_f = fopen(syslog_path, "r");
|
||||
if (messages_f) {
|
||||
fseek(messages_f, 0, SEEK_END);
|
||||
}
|
||||
}
|
||||
|
||||
/* execute CAS command */
|
||||
result = commands[cmd].handle();
|
||||
ftime(&t1);
|
||||
timespan = (1000 * (t1.time - t0.time) + t1.millitm - t0.millitm);
|
||||
|
||||
if (commands[cmd].short_name != 'V') {
|
||||
log_command(argc, argv, result, timespan);
|
||||
}
|
||||
|
||||
/* print extra warning message IF command ended with failure and
|
||||
* syslog contains something */
|
||||
if (FAILURE == result && messages_f) {
|
||||
char line_buf[MAX_STR_LEN];
|
||||
bool kernel_said = false; /* set to true if CAS kernel module
|
||||
* said anything during command
|
||||
* execution */
|
||||
while (fgets(line_buf, MAX_STR_LEN - 1, messages_f)) {
|
||||
line_buf[MAX_STR_LEN - 1] = 0;
|
||||
if (strstr(line_buf, "CAS") &&
|
||||
strstr(line_buf, "kernel")) {
|
||||
kernel_said = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (kernel_said) {
|
||||
fprintf(stderr, "Error occurred, "
|
||||
"please see syslog (%s) for details. \n",
|
||||
syslog_path);
|
||||
}
|
||||
}
|
||||
|
||||
if (messages_f) {
|
||||
fclose(messages_f);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int count_arg_params(const char **argv, int argc)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < argc; i++) {
|
||||
if ('-' == argv[i][0] && 0 != argv[i][1]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
void configure_cli_commands(cli_command *commands)
|
||||
{
|
||||
cli_command *cmd = commands;
|
||||
int ret;
|
||||
|
||||
while(cmd->name) {
|
||||
if (cmd->configure) {
|
||||
ret = cmd->configure(cmd);
|
||||
if (ret < 0) {
|
||||
cmd->flags |= CLI_COMMAND_HIDDEN;
|
||||
}
|
||||
}
|
||||
cmd++;
|
||||
}
|
||||
}
|
||||
|
||||
int args_parse(app *app_values, cli_command *commands, int argc, const char **argv)
|
||||
{
|
||||
int i, j, k, status = SUCCESS;
|
||||
int args_count, args_offset;
|
||||
const char **args_list = NULL;
|
||||
const char* cmd_name = argv[1];
|
||||
cli_ns_entry *entry = NULL;
|
||||
cli_option *options;
|
||||
int cmd, first_opt;
|
||||
|
||||
if (argc < 2) {
|
||||
cas_printf(LOG_ERR, "No command given.\n");
|
||||
print_info(app_values);
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
if (args_is_unrecognized(cmd_name)) {
|
||||
cas_printf(LOG_ERR, "Unrecognized command %s\n", cmd_name);
|
||||
print_info(app_values);
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
for (i = 0;; ++i) {
|
||||
if (commands[i].name == NULL) {
|
||||
if (is_help(cmd_name)) {
|
||||
print_help(app_values, commands);
|
||||
return SUCCESS;
|
||||
} else {
|
||||
cas_printf(LOG_ERR, "Unrecognized command %s\n",
|
||||
cmd_name);
|
||||
print_info(app_values);
|
||||
}
|
||||
return FAILURE;
|
||||
} else if (args_is(cmd_name, commands[i].name, commands[i].short_name)) {
|
||||
cmd = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
configure_cli_commands(commands);
|
||||
|
||||
if (argc >= 3 && get_help_position(argc, argv) != -1) {
|
||||
if (!is_command_hidden(commands, i)) {
|
||||
print_command_help(app_values, &commands[i]);
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
if (is_su_requied(commands, cmd)) {
|
||||
if (getuid() != 0) {
|
||||
cas_printf(LOG_ERR, "Must be run as root.\n");
|
||||
return FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (commands[cmd].options) {
|
||||
options = commands[cmd].options;
|
||||
first_opt = 2;
|
||||
} else if (commands[cmd].namespace) {
|
||||
if (argc < 3) {
|
||||
cas_printf(LOG_ERR, "Missing namespace option.\n");
|
||||
print_info(app_values);
|
||||
return FAILURE;
|
||||
}
|
||||
if (argc < 4) {
|
||||
cas_printf(LOG_ERR, "Missing namespace name.\n");
|
||||
print_info(app_values);
|
||||
return FAILURE;
|
||||
}
|
||||
if (!args_is(argv[2], commands[cmd].namespace->long_name,
|
||||
commands[cmd].namespace->short_name)) {
|
||||
cas_printf(LOG_ERR, "Unrecognized option.\n");
|
||||
print_info(app_values);
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
entry = commands[cmd].namespace->entries;
|
||||
while (true) {
|
||||
if (!strcmp(argv[3], entry->name))
|
||||
break;
|
||||
if (!(++entry)->name) {
|
||||
cas_printf(LOG_ERR, "Unrecognized namespace entry.\n");
|
||||
print_info(app_values);
|
||||
return FAILURE;
|
||||
}
|
||||
}
|
||||
options = entry->options;
|
||||
first_opt = 4;
|
||||
} else {
|
||||
return run_command(commands, cmd, argc, argv);
|
||||
}
|
||||
|
||||
/* for each possible option:
|
||||
* - if it is required, check if it is supplied exactly once
|
||||
* - if it is not required, check if it is supplied at most once
|
||||
*/
|
||||
for (i = 0; options[i].long_name; ++i) {
|
||||
char option_name[MAX_STR_LEN];
|
||||
|
||||
/* count occurrences of an option (k as counter) */
|
||||
k = 0;
|
||||
for (j = first_opt; j < argc; ++j) {
|
||||
if (args_is(argv[j], options[i].long_name,
|
||||
options[i].short_name)) {
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
command_name_with_slash(option_name, MAX_STR_LEN,
|
||||
options[i].short_name, options[i].long_name);
|
||||
|
||||
if (options[i].flags & CLI_OPTION_REQUIRED) {
|
||||
if (!k) {
|
||||
cas_printf(LOG_ERR, "Missing required option %s\n", option_name);
|
||||
print_info(app_values);
|
||||
return FAILURE;
|
||||
}
|
||||
}
|
||||
if (k > 1) {
|
||||
cas_printf(LOG_ERR, "Option supplied more than once %s\n", option_name);
|
||||
print_info(app_values);
|
||||
return FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Store parameters for arguments. Terminate each list with NULL element.
|
||||
* Accomodate for max no of parameters */
|
||||
args_list = malloc(sizeof(*args_list) * (argc + 1));
|
||||
|
||||
if (args_list == NULL) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
/* iterate over all arguments that were actually passed to the CLI */
|
||||
args_count = args_offset = 0;
|
||||
for (i = first_opt; i < argc; ++i) {
|
||||
int opt;
|
||||
|
||||
if (args_is_unrecognized(argv[i])) {
|
||||
cas_printf(LOG_ERR, "Invalid format %s\n",
|
||||
argv[i]);
|
||||
print_info(app_values);
|
||||
|
||||
status = FAILURE;
|
||||
goto free_args;
|
||||
}
|
||||
|
||||
opt = get_option(options, argv[i]);
|
||||
if (opt == -1) {
|
||||
cas_printf(LOG_ERR, "Unrecognized option %s\n",
|
||||
argv[i]);
|
||||
print_info(app_values);
|
||||
|
||||
status = FAILURE;
|
||||
goto free_args;
|
||||
}
|
||||
|
||||
if (options[opt].arg != NULL) {
|
||||
|
||||
/* Count params for current argument. */
|
||||
args_count = count_arg_params(&argv[i + 1], argc - i - 1);
|
||||
|
||||
if (args_count == 0 && !(options[opt].flags & CLI_OPTION_OPTIONAL_ARG)) {
|
||||
|
||||
cas_printf(LOG_ERR, "Missing required argument in %s\n",
|
||||
argv[i]);
|
||||
print_info(app_values);
|
||||
|
||||
status = FAILURE;
|
||||
goto free_args;
|
||||
}
|
||||
|
||||
if (-1 != options[opt].args_count &&
|
||||
args_count != options[opt].args_count &&
|
||||
(0 != args_count || !(options[opt].flags & CLI_OPTION_OPTIONAL_ARG))) {
|
||||
|
||||
cas_printf(LOG_ERR, "Invalid number of arguments for %s\n",
|
||||
argv[i]);
|
||||
print_info(app_values);
|
||||
|
||||
status = FAILURE;
|
||||
goto free_args;
|
||||
}
|
||||
|
||||
/* Add params for current argument.
|
||||
* Terminate list with NULL element.*/
|
||||
for (k = args_offset, j = 0; j < args_count; j++) {
|
||||
args_list[k++] = argv[j + i + 1];
|
||||
}
|
||||
args_list[args_offset + args_count] = NULL;
|
||||
|
||||
i += args_count;
|
||||
}
|
||||
|
||||
if (commands[cmd].command_handle_opts) {
|
||||
status = commands[cmd].command_handle_opts(
|
||||
options[opt].long_name,
|
||||
&args_list[args_offset]);
|
||||
} else if (commands[cmd].namespace_handle_opts) {
|
||||
status = commands[cmd].namespace_handle_opts(
|
||||
entry->name,
|
||||
options[opt].long_name,
|
||||
&args_list[args_offset]);
|
||||
} else {
|
||||
cas_printf(LOG_ERR, "Internal error\n");
|
||||
status = FAILURE;
|
||||
goto free_args;
|
||||
}
|
||||
args_offset += args_count;
|
||||
|
||||
if (0 != status) {
|
||||
cas_printf(LOG_ERR, "Error during options handling\n");
|
||||
print_info(app_values);
|
||||
|
||||
status = FAILURE;
|
||||
goto free_args;
|
||||
}
|
||||
}
|
||||
|
||||
status = run_command(commands, cmd, argc, argv);
|
||||
|
||||
free_args:
|
||||
if (NULL != args_list) {
|
||||
free(args_list);
|
||||
args_list = NULL;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
133
casadm/argp.h
Normal file
133
casadm/argp.h
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef _ARGP_H
|
||||
#define _ARGP_H
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
enum CLI_OPTION_FLAGS {
|
||||
CLI_OPTION_REQUIRED = 1 << 0,
|
||||
CLI_OPTION_HIDDEN = 1 << 1,
|
||||
CLI_OPTION_RANGE_INT = 1 << 2, /*! if option has a min/max value */
|
||||
CLI_OPTION_DEFAULT_INT = 1 << 3, /*! if option has a default value */
|
||||
CLI_OPTION_OPTIONAL_ARG = 1 << 4 /*! if option argument is optional */
|
||||
};
|
||||
|
||||
enum CLI_COMMAND_FLAGS {
|
||||
CLI_SU_REQUIRED = 1 << 0,
|
||||
CLI_COMMAND_HIDDEN = 1 << 1
|
||||
};
|
||||
|
||||
#define ERROR -1
|
||||
#define SUCCESS 0
|
||||
#define FAILURE 1
|
||||
|
||||
/**
|
||||
* structure repsesenting each single option for CLI command (i.e. -i, -j for -R)
|
||||
*/
|
||||
typedef struct {
|
||||
char short_name; /*!< short option name, one-letter. i.e. 'i' representing -i
|
||||
*!< as --cache-id */
|
||||
char* long_name; /*!< long option name (in above described case it would be
|
||||
*!< "cache-id" */
|
||||
char* desc; /*!< description of an option (longer text...
|
||||
*!< may contain single %d for default value and/or pair of %d marks
|
||||
*!< for range of correct values. If it has both, default must come
|
||||
*!< after the range, so be careful about wording such messages) */
|
||||
int args_count; /*!< number of arguments (0 - no arguments, -1 - unspecified) */
|
||||
char* arg; /*!< type of an argument, descriptive. i.e. "NUM", "NAME" */
|
||||
int flags; /*!< as per CLI_OPTION_FLAGS */
|
||||
int min_value; /*!< min parameter value. (optional) */
|
||||
int max_value; /*!< max parameter value. (optional) */
|
||||
int default_value; /*!< default parameter value. (optional) */
|
||||
int priv; /*!< Private filed for command handler */
|
||||
} cli_option;
|
||||
|
||||
/*
|
||||
* In namespace entries options array is nested in another flexible array
|
||||
* (array of entries), so it cannot be flexible array itself. Because of that
|
||||
* we make it static array of options with reasonable lenght.
|
||||
*/
|
||||
#define MAX_OPTIONS 32
|
||||
|
||||
typedef struct {
|
||||
char* name; /*!< namespace entry name */
|
||||
char* desc; /*!< description of an namespace entry */
|
||||
cli_option options[MAX_OPTIONS];
|
||||
/*!< pointer to first element in null-terminated array of cli_option */
|
||||
} cli_ns_entry;
|
||||
|
||||
typedef struct {
|
||||
char short_name; /*!< short name of namespace */
|
||||
char* long_name; /*!< long name of namespace */
|
||||
cli_ns_entry entries[]; /*!< null-terminated array of namespace entries */
|
||||
} cli_namespace;
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
char* info;
|
||||
char* title;
|
||||
char* doc;
|
||||
char* man;
|
||||
int block;
|
||||
} app;
|
||||
|
||||
struct _cli_command;
|
||||
typedef struct _cli_command cli_command;
|
||||
|
||||
/**
|
||||
* structure representing each CLI command, i.e. -S, -T...
|
||||
*/
|
||||
struct _cli_command {
|
||||
char* name; /*!< name of command (i.e. "start-cache" for --start-cache) */
|
||||
|
||||
char short_name; /*!< short name of command (i.e. "S" for -S/--start-cache) */
|
||||
|
||||
char* desc; /*!< description that appears with "casadm -H" invocation */
|
||||
|
||||
char* long_desc; /*!< option descripotion that appears with "casadm -O -H invocation */
|
||||
|
||||
cli_option* options; /*!< pointer to first element in null-terminated array of cli_option */
|
||||
|
||||
int (*command_handle_opts)(char*, const char**);
|
||||
/*! function pointer to function that processes options to command */
|
||||
|
||||
cli_namespace* namespace;
|
||||
/*! namespace description */
|
||||
|
||||
int (*namespace_handle_opts)(char*, char*, const char**);
|
||||
/*! function pointer to function that processes options to namespace */
|
||||
|
||||
int (*handle)(void); /*! function pointer to function that executes a command */
|
||||
|
||||
int flags; /*! command flags, as per CLI_COMMAND_FLAGS */
|
||||
|
||||
void (*help)(app *app_values, cli_command *cmd);
|
||||
/*! Custom help provider */
|
||||
int (*configure)(cli_command *cmd);
|
||||
/*! function pointer to function that configures command */
|
||||
};
|
||||
|
||||
char *command_name_in_brackets(char *buf, size_t buf_size, char short_name, char *long_name);
|
||||
|
||||
void print_help(const app *app_values, const cli_command *commands);
|
||||
|
||||
void print_options_usage(cli_option* options, const char *separator,
|
||||
int (*view)(cli_option* options, int flag), int flag);
|
||||
|
||||
void print_list_options(cli_option* options, int flag,
|
||||
int (*view)(cli_option* options, int flag));
|
||||
|
||||
void print_command_header(const app *app_values, const cli_command *cmd);
|
||||
|
||||
void configure_cli_commands(cli_command *commands);
|
||||
|
||||
int args_parse(app *app_values, cli_command *commands, int argc, const char **argv);
|
||||
|
||||
#endif
|
2860
casadm/cas_lib.c
Normal file
2860
casadm/cas_lib.c
Normal file
File diff suppressed because it is too large
Load Diff
297
casadm/cas_lib.h
Normal file
297
casadm/cas_lib.h
Normal file
@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef __CAS_LIB_H__
|
||||
#define __CAS_LIB_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <syslog.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
#include "safeclib/safe_str_lib.h"
|
||||
#include <cas_ioctl_codes.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#define CTRL_DEV_PATH "/dev/cas_ctrl"
|
||||
|
||||
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
|
||||
|
||||
#define FAILURE 1 /**< default non-zero exit code. */
|
||||
#define INTERRUPTED 2 /**< if command is interrupted */
|
||||
#define SUCCESS 0 /**< 0 exit code from majority of our functions
|
||||
stands for success */
|
||||
|
||||
struct core_device {
|
||||
int id;
|
||||
int cache_id;
|
||||
char path[MAX_STR_LEN];
|
||||
struct kcas_core_info info;
|
||||
};
|
||||
|
||||
struct cache_device {
|
||||
int id;
|
||||
int state;
|
||||
int expected_core_count;
|
||||
char device[MAX_STR_LEN];
|
||||
int mode;
|
||||
int eviction_policy;
|
||||
int cleaning_policy;
|
||||
int dirty;
|
||||
int flushed;
|
||||
unsigned size;
|
||||
int core_count;
|
||||
struct core_device cores[];
|
||||
};
|
||||
|
||||
struct cas_param {
|
||||
char *name;
|
||||
char *unit;
|
||||
char **value_names;
|
||||
uint32_t (*transform_value)(uint32_t value);
|
||||
uint32_t value;
|
||||
bool select;
|
||||
};
|
||||
|
||||
enum output_format_t {
|
||||
OUTPUT_FORMAT_INVALID = 0,
|
||||
OUTPUT_FORMAT_TABLE = 1,
|
||||
OUTPUT_FORMAT_CSV = 2,
|
||||
OUTPUT_FORMAT_DEFAULT = OUTPUT_FORMAT_TABLE
|
||||
};
|
||||
|
||||
enum metadata_mode_t {
|
||||
METADATA_MODE_INVALID = 0,
|
||||
METADATA_MODE_NORMAL,
|
||||
METADATA_MODE_ATOMIC,
|
||||
METADATA_MODE_DEFAULT = METADATA_MODE_NORMAL,
|
||||
};
|
||||
|
||||
#define STATS_FILTER_INVALID 0
|
||||
#define STATS_FILTER_CONF (1 << 0)
|
||||
#define STATS_FILTER_USAGE (1 << 1)
|
||||
#define STATS_FILTER_REQ (1 << 2)
|
||||
#define STATS_FILTER_BLK (1 << 3)
|
||||
#define STATS_FILTER_ERR (1 << 4)
|
||||
#define STATS_FILTER_IOCLASS (1 << 5)
|
||||
#define STATS_FILTER_ALL (STATS_FILTER_CONF | \
|
||||
STATS_FILTER_USAGE | \
|
||||
STATS_FILTER_REQ | \
|
||||
STATS_FILTER_BLK | \
|
||||
STATS_FILTER_ERR)
|
||||
#define STATS_FILTER_DEFAULT STATS_FILTER_ALL
|
||||
|
||||
#define STATS_FILTER_COUNTERS (STATS_FILTER_REQ | STATS_FILTER_BLK | STATS_FILTER_ERR)
|
||||
|
||||
const char *eviction_policy_to_name(uint8_t policy);
|
||||
const char *cleaning_policy_to_name(uint8_t policy);
|
||||
const char *cache_mode_to_name(uint8_t cache_mode);
|
||||
const char *get_cache_state_name(int cache_state);
|
||||
const char *get_core_state_name(int core_state);
|
||||
const char *metadata_variant_to_name(uint8_t variant);
|
||||
const char *metadata_mode_to_name(uint8_t metadata_mode);
|
||||
const char *seq_cutoff_policy_to_name(uint8_t seq_cutoff_policy);
|
||||
|
||||
__attribute__((format(printf, 2, 3)))
|
||||
typedef int (*cas_printf_t)(int log_level, const char *format, ...);
|
||||
|
||||
extern cas_printf_t cas_printf;
|
||||
|
||||
__attribute__((format(printf, 2, 3)))
|
||||
int caslog(int log_level, const char *template, ...);
|
||||
|
||||
#define CAS_CLI_HELP_METADATA_VARIANTS \
|
||||
CAS_METADATA_VARIANT_MAX"|" \
|
||||
CAS_METADATA_VARIANT_MIX"|" \
|
||||
CAS_METADATA_VARIANT_MIN
|
||||
|
||||
/* for CLI commands arguments */
|
||||
#define YES 1
|
||||
#define NO 0
|
||||
#define UNDEFINED -1
|
||||
void metadata_memory_footprint(uint64_t size, float *footprint, const char **units);
|
||||
|
||||
int start_cache(ocf_cache_id_t cache_id, unsigned int cache_init,
|
||||
const char *cache_device, ocf_cache_mode_t cache_mode,
|
||||
ocf_eviction_t eviction_policy_type,
|
||||
ocf_cache_line_size_t line_size, int force);
|
||||
int stop_cache(ocf_cache_id_t cache_id, int flush);
|
||||
|
||||
#ifdef WI_AVAILABLE
|
||||
#define CAS_CLI_HELP_START_CACHE_MODES "wt|wb|wa|pt|wi"
|
||||
#define CAS_CLI_HELP_SET_CACHE_MODES "wt|wb|wa|pt|wi"
|
||||
#define CAS_CLI_HELP_SET_CACHE_MODES_FULL "Write-Through, Write-Back, Write-Around, Pass-Through, Write-Invalidate"
|
||||
#define CAS_CLI_HELP_START_CACHE_MODES_FULL "Write-Through, Write-Back, Write-Around, Pass-Through, Write-Invalidate"
|
||||
#else
|
||||
#define CAS_CLI_HELP_START_CACHE_MODES "wt|wb|wa|pt"
|
||||
#define CAS_CLI_HELP_SET_CACHE_MODES "wt|wb|wa|pt"
|
||||
#define CAS_CLI_HELP_START_CACHE_MODES_FULL "Write-Through, Write-Back, Write-Around, Pass-Through"
|
||||
#define CAS_CLI_HELP_SET_CACHE_MODES_FULL "Write-Through, Write-Back, Write-Around, Pass-Through"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief handle set cache param command
|
||||
* @param cache_id id of cache device
|
||||
* @param params parameter array
|
||||
* @return exit code of successful completion is 0;
|
||||
* nonzero exit code means failure
|
||||
*/
|
||||
int cache_params_set(unsigned int cache_id, struct cas_param *params);
|
||||
|
||||
/**
|
||||
* @brief get cache param value
|
||||
* @param cache_id id of cache device
|
||||
* @param param_id id of cache parameter to retrive
|
||||
* @param param variable to pass value to caller
|
||||
* @return exit code of successful completion is 0;
|
||||
* nonzero exit code means failure
|
||||
*/
|
||||
int cache_get_param(unsigned int cache_id, unsigned int param_id,
|
||||
struct cas_param *param);
|
||||
/**
|
||||
* @brief handle get cache param command
|
||||
* @param cache_id id of cache device
|
||||
* @param params parameter array
|
||||
* @return exit code of successful completion is 0;
|
||||
* nonzero exit code means failure
|
||||
*/
|
||||
int cache_params_get(unsigned int cache_id, struct cas_param *params,
|
||||
unsigned int output_format);
|
||||
|
||||
/**
|
||||
* @brief handle set core param command
|
||||
* @param cache_id id of cache device
|
||||
* @param core_id id of core device
|
||||
* @param params parameter array
|
||||
* @return exit code of successful completion is 0;
|
||||
* nonzero exit code means failure
|
||||
*/
|
||||
int core_params_set(unsigned int cache_id, unsigned int core_id,
|
||||
struct cas_param *params);
|
||||
|
||||
/**
|
||||
* @brief handle get core param command
|
||||
* @param cache_id id of cache device
|
||||
* @param core_id id of core device
|
||||
* @param params parameter array
|
||||
* @return exit code of successful completion is 0;
|
||||
* nonzero exit code means failure
|
||||
*/
|
||||
int core_params_get(unsigned int cache_id, unsigned int core_id,
|
||||
struct cas_param *params, unsigned int output_format);
|
||||
|
||||
/**
|
||||
* @brief handle set cache mode (-Q) command
|
||||
* @param in cache mode identifier of cache mode (WRITE_BACK, WRITE_THROUGH etc...)
|
||||
* @param cache_id id of cache device
|
||||
* @param flush whenever we should flush cache during execution of command. Options: YES, NO, UNDEFINED.
|
||||
* (UNDEFINED is illegal when transitioning from Write-Back mode to any other mode)
|
||||
*/
|
||||
int set_cache_mode(unsigned int cache_state, unsigned int cache_id, int flush);
|
||||
|
||||
/**
|
||||
* @brief add core device to a cache
|
||||
*
|
||||
* @param cache_id cache to which new core is being added
|
||||
* @param core_device path to a core device that is being added
|
||||
* @param iogroup_id id of iogroup (this parameter is not exposed in user CLI)
|
||||
* @param try_add try add core to earlier loaded cache or add to core pool
|
||||
* @param update_path try update path to core device
|
||||
* @return 0 upon successful core addition, 1 upon failure
|
||||
*/
|
||||
int add_core(unsigned int cache_id, unsigned int core_id, const char *core_device, int try_add, int update_path);
|
||||
|
||||
int get_core_info(int fd, int cache_id, int core_id, struct kcas_core_info *info);
|
||||
|
||||
int remove_core(unsigned int cache_id, unsigned int core_id,
|
||||
bool detach, bool force_no_flush);
|
||||
|
||||
int core_pool_remove(const char *core_device);
|
||||
int get_core_pool_count(int fd);
|
||||
|
||||
int reset_counters(unsigned int cache_id, unsigned int core_id);
|
||||
|
||||
int flush_cache(unsigned int cache_id);
|
||||
int flush_core(unsigned int cache_id, unsigned int core_id);
|
||||
|
||||
int get_cas_capabilites_quiet(struct kcas_capabilites *caps);
|
||||
int get_cas_capabilites(struct kcas_capabilites *caps);
|
||||
|
||||
int nvme_format(const char *device_path, int metadata_mode, int force);
|
||||
|
||||
int check_cache_device(const char *device_path);
|
||||
|
||||
int partition_list(unsigned int cache_id, unsigned int output_format);
|
||||
int partition_setup(unsigned int cache_id, const char *file);
|
||||
int partition_is_name_valid(const char *name);
|
||||
|
||||
int cas_module_version(char *buff, int size);
|
||||
int disk_module_version(char *buff, int size);
|
||||
int list_caches(unsigned int list_format);
|
||||
int cache_status(unsigned int cache_id, unsigned int core_id, int io_class_id,
|
||||
unsigned int stats_filters, unsigned int stats_format);
|
||||
int get_inactive_core_count(const struct kcas_cache_info *cache_info);
|
||||
|
||||
int open_ctrl_device_quiet();
|
||||
int open_ctrl_device();
|
||||
int *get_cache_ids(int *cache_count);
|
||||
struct cache_device *get_cache_device_by_id_fd(int cache_id, int fd);
|
||||
struct cache_device **get_cache_devices(int *caches_count);
|
||||
void free_cache_devices_list(struct cache_device **caches, int caches_count);
|
||||
|
||||
int validate_dev(const char *dev_path);
|
||||
int validate_str_num(const char *source_str, const char *msg, long long int min, long long int max);
|
||||
int validate_str_num_sbd(const char *source_str, const char *msg, int min, int max);
|
||||
int validate_str_unum(const char *source_str, const char *msg, unsigned int min,
|
||||
unsigned int max);
|
||||
int validate_path(const char *path, int exist);
|
||||
|
||||
int validate_str_cache_mode(const char *s);
|
||||
int validate_str_ev_policy(const char *s);
|
||||
int validate_str_cln_policy(const char *s);
|
||||
int validate_str_meta_variant(const char *s);
|
||||
int validate_str_stats_filters(const char* s);
|
||||
int validate_str_output_format(const char* s);
|
||||
int validate_str_metadata_mode(const char* s);
|
||||
|
||||
/**
|
||||
* @brief calculate flush progress
|
||||
*
|
||||
* @param[in] dirty number of dirty blocks
|
||||
* @param[in] flush number of flushed blocks
|
||||
* @return flush progress or 0 if no flush is ongoing
|
||||
*/
|
||||
float calculate_flush_progress(unsigned dirty, unsigned flushed);
|
||||
|
||||
/**
|
||||
* @brief calculate flush progress of given cache
|
||||
*
|
||||
* @param[in] cache_id cache to which calculation applies
|
||||
* @param[out] progress flush progress
|
||||
* @return 0 on success, nonzero on failure
|
||||
*/
|
||||
int get_flush_progress(int unsigned cache_id, float *progress);
|
||||
|
||||
/**
|
||||
* @brief print error message corresponding with CAS extended error code.
|
||||
*/
|
||||
void print_err(int error_code);
|
||||
|
||||
/**
|
||||
* @brief get special device file path (/dev/sdX) for disk.
|
||||
*/
|
||||
int get_dev_path(const char* disk, char* buf, size_t num);
|
||||
|
||||
/**
|
||||
* @brief convert string to int
|
||||
*/
|
||||
bool str_to_int(const char* start, char** end, int *val);
|
||||
|
||||
#endif
|
535
casadm/cas_lib_utils.c
Normal file
535
casadm/cas_lib_utils.c
Normal file
@ -0,0 +1,535 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <stdbool.h>
|
||||
#include "cas_lib.h"
|
||||
#include "extended_err_msg.h"
|
||||
#include "cas_lib_utils.h"
|
||||
#include "safeclib/safe_str_lib.h"
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <poll.h>
|
||||
#include <execinfo.h>
|
||||
extern cas_printf_t cas_printf;
|
||||
|
||||
#define IOCTL_RETRIES 3 /* this is how many times ioctl is called */
|
||||
|
||||
#define VT100_CLEARLINE "[K"
|
||||
#define ESCAPE 0x1b
|
||||
#define CARRIAGE_RETURN 0xd
|
||||
#define INVALID_DIRTY_NO ((uint64_t) (-1))
|
||||
|
||||
/* file descriptors for pipe */
|
||||
/* 1 is writing end, 0 is reading end of a pipe */
|
||||
int fdspipe[2];
|
||||
/* these must be global for handling signals */
|
||||
volatile static int interrupted = 0; /*!< signal was caught, interrupt the
|
||||
*!< management operation now! */
|
||||
static int finished = 0; /*!< if management operation has finished
|
||||
*!< (so that progressbar drawing thread
|
||||
*!< can either display "100%" or exit quietly */
|
||||
static int device_id = 0; /*!< id of caching device to which management
|
||||
*!< operation that is underway, applies */
|
||||
|
||||
|
||||
/**
|
||||
* Default signal handling function exists so that SIGINT wan't interrupt management
|
||||
* operations in unpredicted/disallowed way.
|
||||
*/
|
||||
void sig_handler_default(int x)
|
||||
{
|
||||
static int inter_counter = 0;
|
||||
inter_counter++;
|
||||
if (inter_counter>4) {
|
||||
cas_printf(LOG_ERR,
|
||||
"Can't interrupt CAS management process\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If management operation was interrupted due to user action (SIGINT)
|
||||
*/
|
||||
int was_ioctl_interrupted()
|
||||
{
|
||||
return interrupted;
|
||||
}
|
||||
|
||||
void sig_handler_interrupt_flushing(int x)
|
||||
{
|
||||
struct kcas_interrupt_flushing cmd_info;
|
||||
int fd = open(CTRL_DEV_PATH, 0);
|
||||
close(fdspipe[1]);
|
||||
interrupted = 1;
|
||||
|
||||
if (fd < 0) {
|
||||
cas_printf(LOG_ERR, "Device " CTRL_DEV_PATH " not found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&cmd_info, 0, sizeof(cmd_info));
|
||||
cmd_info.cache_id = device_id;
|
||||
|
||||
int res =
|
||||
run_ioctl(fd, KCAS_IOCTL_INTERRUPT_FLUSHING, &cmd_info);
|
||||
|
||||
close(fd);
|
||||
if (!res) {
|
||||
set_default_sig_handler();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* print current backtrace
|
||||
*/
|
||||
void dump_stack()
|
||||
{
|
||||
const int sym_max = 512;
|
||||
void *sym_buf[sym_max];
|
||||
int nsym;
|
||||
nsym = backtrace(sym_buf, sym_max);
|
||||
backtrace_symbols_fd(sym_buf, nsym, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sad CAS :(
|
||||
* dump stack to allow debugging.
|
||||
*/
|
||||
void segv_handler_default(int i)
|
||||
{
|
||||
cas_printf(LOG_ERR, "Segmentation fault\n");
|
||||
dump_stack();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/**
|
||||
* register default signal handling function
|
||||
*/
|
||||
void set_default_sig_handler()
|
||||
{
|
||||
signal(SIGINT, sig_handler_default);
|
||||
signal(SIGSEGV, segv_handler_default);
|
||||
}
|
||||
|
||||
/**
|
||||
* handle errors of cafe c library (wrong parameters passed)
|
||||
*/
|
||||
static void safe_lib_constraint_handler(const char *msg, void *ptr, errno_t error)
|
||||
{
|
||||
cas_printf(LOG_ERR, "Safe C lib error\n");
|
||||
if (msg) {
|
||||
cas_printf(LOG_ERR, "%s (%d)\n", msg, error);
|
||||
}
|
||||
dump_stack();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register constraint handler for safe_string_library
|
||||
*/
|
||||
void set_safe_lib_constraint_handler()
|
||||
{
|
||||
set_mem_constraint_handler_s(safe_lib_constraint_handler);
|
||||
set_str_constraint_handler_s(safe_lib_constraint_handler);
|
||||
}
|
||||
|
||||
int _open_ctrl_device(int quiet)
|
||||
{
|
||||
int fd;
|
||||
fd = open(CTRL_DEV_PATH, 0);
|
||||
|
||||
if (fd < 0) {
|
||||
if (!quiet) {
|
||||
cas_printf(LOG_ERR, "Device " CTRL_DEV_PATH
|
||||
" not found\n");
|
||||
cas_printf(LOG_INFO, "Is the kernel module loaded?\n");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int open_ctrl_device_quiet()
|
||||
{
|
||||
return _open_ctrl_device(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* calls open on control device; returns either error (-1) or a valid file descriptor
|
||||
*/
|
||||
int open_ctrl_device()
|
||||
{
|
||||
return _open_ctrl_device(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief print spinning wheel
|
||||
*/
|
||||
void print_progress_indicator(float prog, struct progress_status *ps)
|
||||
{
|
||||
static const char prog_indicator[] = { '|', '/', '-', '\\', '|', '/', '-', '\\'};
|
||||
/*!< set of characters implementing "spinning wheel" progress indicator */
|
||||
static int max_i = ARRAY_SIZE(prog_indicator);
|
||||
static int i = 0;
|
||||
|
||||
printf("%c%s... [%c]%c" VT100_CLEARLINE,
|
||||
CARRIAGE_RETURN, ps->friendly_name, prog_indicator[i], ESCAPE);
|
||||
if (50 < prog) {
|
||||
/* we're almost there. Ignore all signals at this stage */
|
||||
set_default_sig_handler();
|
||||
}
|
||||
|
||||
i = (i + 1) % max_i;
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief print progress bar once
|
||||
* @param prog degree of progress (0-100)
|
||||
* @param ps structure holding status between progressbar and caller
|
||||
*/
|
||||
void print_progress_bar(float prog, struct progress_status *ps)
|
||||
{
|
||||
/* constants affecting look of progressbar/activity indicator */
|
||||
static const char progress_full = '='; /*!< represents progress_step of progress */
|
||||
static const char progress_partial = '-';/*!< represents progress of more than 0
|
||||
*!< but less than progress_step */
|
||||
static const char progress_empty = ' '; /*!< progressbar didn't reach here */
|
||||
static const char delimiter_left = '['; /*!< left delimiter of progress bar */
|
||||
static const char delimiter_right = ']';/*!< right delimiter of progress bar */
|
||||
static const int progress_step = 2; /*!< progress step - percentage of progress to
|
||||
*!< be represented by one character. i.e. if
|
||||
*!< progress stepis set to 2, entire
|
||||
*!< progressbar is 50 chars wide+2 chars for
|
||||
*!< delimiters */
|
||||
|
||||
int i, remaining_m;
|
||||
time_t elapsed, remaining_s;
|
||||
|
||||
printf("%c%s... ", CARRIAGE_RETURN, ps->friendly_name);
|
||||
/* carriage return and "name of op"*/
|
||||
putchar(delimiter_left);
|
||||
|
||||
/* make sure, progressbar always moves forward and never backward */
|
||||
if (prog < ps->progress_accumulated) {
|
||||
prog = ps->progress_accumulated;
|
||||
} else {
|
||||
ps->progress_accumulated = prog;
|
||||
}
|
||||
|
||||
/* print actual progress bar */
|
||||
for (i = progress_step; i <= prog; i += progress_step){
|
||||
putchar(progress_full);
|
||||
}
|
||||
|
||||
if (((int)prog) % progress_step) {
|
||||
putchar(progress_partial);
|
||||
i += progress_step;
|
||||
}
|
||||
|
||||
for (; i <= 100; i += progress_step){
|
||||
putchar(progress_empty);
|
||||
}
|
||||
|
||||
elapsed = time(NULL) - ps->time_started;
|
||||
|
||||
remaining_s = ((100 - prog) * elapsed) / (prog ?: 1);
|
||||
remaining_m = remaining_s / 60;
|
||||
remaining_s -= remaining_m * 60;
|
||||
|
||||
if (remaining_m) {
|
||||
/* ESCAPE VT100_CLEARLINE is terminal control sequence to clear "rest
|
||||
* of the line */
|
||||
printf("%c %3.1f%% [%dm%02lds remaining]%c" VT100_CLEARLINE,
|
||||
delimiter_right, prog, remaining_m, remaining_s, ESCAPE);
|
||||
} else {
|
||||
printf("%c %3.1f%% [%lds remaining]%c" VT100_CLEARLINE,
|
||||
delimiter_right, prog, remaining_s, ESCAPE);
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief either print a progressbar or spinning wheel depending on prog
|
||||
*/
|
||||
void print_progress_bar_or_indicator(float prog, struct progress_status *ps)
|
||||
{
|
||||
if (0.01 > prog || 99.99 < prog) {
|
||||
print_progress_indicator(prog, ps);
|
||||
} else {
|
||||
print_progress_bar(prog, ps);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* initialize progressbar structure;
|
||||
*/
|
||||
void init_progress_bar(struct progress_status *ps)
|
||||
{
|
||||
if (NULL != ps) {
|
||||
memset(ps, 0, sizeof(*ps));
|
||||
ps->dirty_clines_curr = INVALID_DIRTY_NO;
|
||||
ps->dirty_clines_initial = INVALID_DIRTY_NO;
|
||||
ps->time_started = time(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void get_core_flush_progress(int fd, int cache_id, int core_id, float *prog)
|
||||
{
|
||||
struct kcas_core_info cmd_info;
|
||||
|
||||
memset(&cmd_info, 0, sizeof(cmd_info));
|
||||
cmd_info.cache_id = cache_id;
|
||||
cmd_info.core_id = core_id;
|
||||
|
||||
if (0 == ioctl(fd, KCAS_IOCTL_CORE_INFO, &cmd_info)) {
|
||||
*prog = calculate_flush_progress(cmd_info.stats.dirty,
|
||||
cmd_info.stats.flushed);
|
||||
}
|
||||
}
|
||||
|
||||
void get_cache_flush_progress(int fd, int cache_id, float *prog)
|
||||
{
|
||||
struct kcas_cache_info cmd_info;
|
||||
|
||||
memset(&cmd_info, 0, sizeof(cmd_info));
|
||||
cmd_info.cache_id = cache_id;
|
||||
|
||||
if (0 == ioctl(fd, KCAS_IOCTL_CACHE_INFO, &cmd_info)) {
|
||||
*prog = calculate_flush_progress(cmd_info.info.dirty,
|
||||
cmd_info.info.flushed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pthread thread handling function - runs during proper ioctl execution. Prints command progress
|
||||
*/
|
||||
void *print_command_progress(void *th_arg)
|
||||
{
|
||||
static const int
|
||||
show_progressbar_after = 2; /*!< threshold in seconds */
|
||||
|
||||
int do_print_progress_bar = 0;
|
||||
int mseconds = 0; /*< milliseconds */
|
||||
int fd;
|
||||
float prog = 0.;
|
||||
struct progress_status *ps = th_arg;
|
||||
/* argument of command progress of which is monitored */
|
||||
/*1,2,0 are descriptors of stdout, err and in respectively*/
|
||||
int running_tty = isatty(1) && isatty(2) && isatty(0);
|
||||
struct sigaction new_action, old_action;
|
||||
|
||||
fd = open(CTRL_DEV_PATH, 0);
|
||||
if (fd < 0) {
|
||||
cas_printf(LOG_ERR, "Device " CTRL_DEV_PATH " not found\n");
|
||||
return NULL; /* FAILURE; */
|
||||
}
|
||||
|
||||
device_id = ps->cache_id;
|
||||
|
||||
sigaction(SIGINT, NULL, &old_action);
|
||||
if (old_action.sa_handler != SIG_IGN) {
|
||||
new_action.sa_handler = sig_handler_interrupt_flushing;
|
||||
sigemptyset(&new_action.sa_mask);
|
||||
new_action.sa_flags = 0;
|
||||
sigaction(SIGINT, &new_action, NULL);
|
||||
}
|
||||
|
||||
sched_yield();
|
||||
|
||||
while (1) {
|
||||
struct pollfd pfd;
|
||||
struct timespec ts;
|
||||
sigset_t sigmask;
|
||||
int ppoll_res;
|
||||
sigemptyset(&sigmask);
|
||||
ts.tv_sec = 1;
|
||||
ts.tv_nsec = 0;
|
||||
pfd.fd = fdspipe[0];
|
||||
pfd.events = POLLIN | POLLRDHUP;
|
||||
ppoll_res = ppoll(&pfd, 1, &ts, &sigmask);
|
||||
if (ppoll_res < 0) {
|
||||
if (ENOMEM == errno) {
|
||||
sleep(1);
|
||||
/* ppoll call failed due to insufficient memory */
|
||||
} else if (EINTR == errno) {
|
||||
interrupted = 1;
|
||||
} else { /* other error conditions are EFAULT or EINVAL
|
||||
* cannot happen in realistic conditions,
|
||||
* and are likely to refer to OS errors, which
|
||||
* cannot possibly be handled. Perform abortion.
|
||||
*/
|
||||
cas_printf(LOG_ERR, "Failed ppoll");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
mseconds += 1000;
|
||||
|
||||
if (interrupted) {
|
||||
/* if flushing is interrupted by signal, don't proceed with displaying
|
||||
* any kind of progress bar. if bar was previously printed,
|
||||
* print indicator instead */
|
||||
if (do_print_progress_bar) {
|
||||
print_progress_indicator(100, ps);
|
||||
}
|
||||
break;
|
||||
} else if (finished) {
|
||||
if (do_print_progress_bar) {
|
||||
print_progress_bar_or_indicator(100., ps);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (ps->core_id == OCF_CORE_ID_INVALID) {
|
||||
get_cache_flush_progress(fd, ps->cache_id, &prog);
|
||||
} else {
|
||||
get_core_flush_progress(fd, ps->cache_id, ps->core_id, &prog);
|
||||
}
|
||||
|
||||
/* it is normal that ioctl to get statistics
|
||||
* fails from time to time. Most common cases
|
||||
* of it are:
|
||||
* - during --start-cache when cache isn't added
|
||||
* - during --stopping-cache, when progress is
|
||||
* supposed to read "100%", but cache is actually
|
||||
* already removed and its stopping progress can't
|
||||
* be queried at all.
|
||||
*/
|
||||
if (mseconds >= show_progressbar_after * 1000
|
||||
&& running_tty && prog < 50) {
|
||||
do_print_progress_bar = 1;
|
||||
}
|
||||
|
||||
if (do_print_progress_bar) {
|
||||
print_progress_bar_or_indicator(prog, ps);
|
||||
}
|
||||
}
|
||||
close(fdspipe[0]);
|
||||
|
||||
close(fd);
|
||||
|
||||
/* if progressbar was displayed at least one, clear line */
|
||||
if (do_print_progress_bar) {
|
||||
printf("%c%c" VT100_CLEARLINE, CARRIAGE_RETURN, ESCAPE);
|
||||
}
|
||||
fflush(stdout);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Run ioctl in a way that displays progressbar (if flushing operation takes longer)
|
||||
* Catch SIGINT signal.
|
||||
* @param friendly_name name of management operation that shall
|
||||
* be displayed in command prompt
|
||||
*/
|
||||
int run_ioctl_interruptible(int fd, int command, void *cmd,
|
||||
char *friendly_name, int cache_id, int core_id)
|
||||
{
|
||||
pthread_t thread;
|
||||
int ioctl_res;
|
||||
struct progress_status ps;
|
||||
sigset_t sigset;
|
||||
|
||||
init_progress_bar(&ps);
|
||||
ps.friendly_name = friendly_name;
|
||||
ps.cache_id = cache_id;
|
||||
ps.core_id = core_id;
|
||||
if (pipe(fdspipe)) {
|
||||
cas_printf(LOG_ERR,"Failed to allocate pipes.\n");
|
||||
return -1;
|
||||
}
|
||||
interrupted = 0;
|
||||
|
||||
sigemptyset(&sigset);
|
||||
sigaddset(&sigset, SIGINT);
|
||||
pthread_sigmask(SIG_BLOCK, &sigset, NULL);
|
||||
|
||||
pthread_create(&thread, 0, print_command_progress, &ps);
|
||||
ioctl_res = run_ioctl(fd, command, cmd);
|
||||
if (!interrupted) {
|
||||
close(fdspipe[1]);
|
||||
}
|
||||
finished = 1;
|
||||
|
||||
pthread_join(thread, 0);
|
||||
|
||||
return ioctl_res;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief ioctl wrapper that retries ioctl attempts within one second timeouts
|
||||
* @param[in] fd as for IOCTL(2)
|
||||
* @param[in] command as for IOCTL(2)
|
||||
* @param[inout] cmd_info as for IOCTL(2)
|
||||
*/
|
||||
int run_ioctl(int fd, int command, void *cmd)
|
||||
{
|
||||
int i, ret;
|
||||
struct timespec timeout = {
|
||||
.tv_sec = 1,
|
||||
.tv_nsec = 0,
|
||||
};
|
||||
|
||||
for (i = 0; i < IOCTL_RETRIES; i++) {
|
||||
ret = ioctl(fd, command, cmd);
|
||||
|
||||
if (ret < 0) {
|
||||
if (interrupted) {
|
||||
return -EINTR;
|
||||
} if (EINTR == errno) {
|
||||
return -EINTR;
|
||||
} else if (EBUSY == errno) {
|
||||
int nret = nanosleep(&timeout, NULL);
|
||||
if (nret) {
|
||||
return -EINTR;
|
||||
}
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int create_pipe_pair(FILE **intermediate_file)
|
||||
{
|
||||
/* 1 is writing end, 0 is reading end of a pipe */
|
||||
int pipefd[2];
|
||||
|
||||
if (pipe(pipefd)) {
|
||||
cas_printf(LOG_ERR,"Failed to create unidirectional pipe.\n");
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
intermediate_file[0] = fdopen(pipefd[0], "r");
|
||||
if (!intermediate_file[0]) {
|
||||
cas_printf(LOG_ERR,"Failed to open reading end of an unidirectional pipe.\n");
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
return FAILURE;
|
||||
}
|
||||
intermediate_file[1] = fdopen(pipefd[1], "w");
|
||||
if (!intermediate_file[1]) {
|
||||
cas_printf(LOG_ERR,"Failed to open reading end of an unidirectional pipe.\n");
|
||||
fclose(intermediate_file[0]);
|
||||
close(pipefd[1]);
|
||||
return FAILURE;
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
68
casadm/cas_lib_utils.h
Normal file
68
casadm/cas_lib_utils.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef __CAS_LIB_UTILS_H__
|
||||
#define __CAS_LIB_UTILS_H__
|
||||
|
||||
struct progress_status {
|
||||
uint64_t
|
||||
dirty_clines_initial; /*!< amount of dirty clines when command is initiated */
|
||||
|
||||
uint64_t
|
||||
dirty_clines_curr; /*!< amount of dirty clines at current progress level */
|
||||
|
||||
int progress_accumulated; /*!< this is to ensure that progressbar is always
|
||||
*!< from 0 to 100% and progress indicated by it
|
||||
*!< never actually drops. */
|
||||
int time_started; /*!< time when particular long running
|
||||
*!< operation was started */
|
||||
char *friendly_name; /*!< name of management operation that shall
|
||||
*!< be displayed in command prompt */
|
||||
int cache_id; /*!< cache id */
|
||||
int core_id; /*!< core id */
|
||||
};
|
||||
|
||||
|
||||
void init_progress_bar(struct progress_status *ps);
|
||||
void print_progress_bar_or_indicator(float prog, struct progress_status *ps);
|
||||
int run_ioctl(int fd, int command, void *cmd);
|
||||
int run_ioctl_interruptible(int fd, int command, void *cmd,
|
||||
char *friendly_name, int cache_id, int core_id);
|
||||
int open_ctrl_device();
|
||||
int was_ioctl_interrupted();
|
||||
void set_default_sig_handler();
|
||||
void set_safe_lib_constraint_handler();
|
||||
|
||||
|
||||
/**
|
||||
* function creates pair files representing an unnamed pipe.
|
||||
* this is highlevel counterpart to pipe syscall.
|
||||
*
|
||||
* null is returned upon failure;
|
||||
*
|
||||
* FILE *pipes[2] is returned upon success.
|
||||
* 1 is writing end, 0 is reading end of a pipe
|
||||
*/
|
||||
int create_pipe_pair(FILE **);
|
||||
|
||||
/**
|
||||
* Check if string is empty
|
||||
*
|
||||
* @param str - reference to the string
|
||||
* @retval 1 string is empty
|
||||
* @retval 0 string is not empty
|
||||
*/
|
||||
static inline int strempty(const char *str)
|
||||
{
|
||||
if (NULL == str) {
|
||||
return 1;
|
||||
} else if ('\0' == str[0]) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
2024
casadm/cas_main.c
Normal file
2024
casadm/cas_main.c
Normal file
File diff suppressed because it is too large
Load Diff
483
casadm/csvparse.c
Normal file
483
casadm/csvparse.c
Normal file
@ -0,0 +1,483 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
#include "csvparse.h"
|
||||
#include "cas_lib_utils.h"
|
||||
#include "safeclib/safe_lib.h"
|
||||
#include <cas_ioctl_codes.h>
|
||||
|
||||
#define SUCCESS 0
|
||||
#define FAILURE 1
|
||||
|
||||
struct CSVFILE_t {
|
||||
FILE *f; /**< underlying byte stream*/
|
||||
int num_columns; /**< number of columns in recently read
|
||||
line of CSV file */
|
||||
int alloc_column_ptrs; /**< number of pointers to columns
|
||||
that can be fit in columns buffer */
|
||||
char **columns; /**< buffer contains exactly one pointer to each
|
||||
column of a csv file */
|
||||
char *buffer; /**< buffer to which recently read line of a csv file
|
||||
is stored */
|
||||
int buffer_size; /**< size of a buffer */
|
||||
|
||||
char csv_comment; /**< character markng whole line comment. if set to null,
|
||||
comments in file are not respected */
|
||||
char csv_separator; /**< csv separator (by default coma, but in some csv formats
|
||||
it is something different */
|
||||
};
|
||||
|
||||
#define DEF_ALLOC_COL_PTRS 2
|
||||
#define DEF_CSV_FILE_BUFFER_SIZE 20
|
||||
|
||||
/* return error when input dataset size exceeds some common sense limitations */
|
||||
#define MAX_NUM_COLUMNS 100
|
||||
#define MAX_LINE_LENGTH 8192
|
||||
|
||||
CSVFILE *csv_open(const char *path, const char *mode)
|
||||
{
|
||||
CSVFILE *csv;
|
||||
|
||||
if (!path || !mode) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* open underlying file as a character stream */
|
||||
FILE *f = fopen(path, mode);
|
||||
if (!f) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
csv = csv_fopen(f);
|
||||
if (NULL == csv) {
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return csv;
|
||||
}
|
||||
|
||||
CSVFILE *csv_fopen(FILE *f)
|
||||
{
|
||||
CSVFILE *cf = malloc(sizeof(*cf));
|
||||
if (!cf) {
|
||||
return NULL;
|
||||
}
|
||||
/* allocate storage for columns of CSV file */
|
||||
cf->num_columns = 0;
|
||||
cf->alloc_column_ptrs = DEF_ALLOC_COL_PTRS;
|
||||
|
||||
cf->columns = malloc(cf->alloc_column_ptrs * sizeof(char *));
|
||||
if (!cf->columns) {
|
||||
free(cf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* allocate storage for line of CSV file */
|
||||
cf->buffer_size = DEF_CSV_FILE_BUFFER_SIZE;
|
||||
cf->buffer = malloc(cf->buffer_size);
|
||||
if (!cf->buffer) {
|
||||
free(cf->columns);
|
||||
free(cf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* assign underlying file as a character stream */
|
||||
cf->f = f;
|
||||
|
||||
cf->csv_separator = ',';
|
||||
cf->csv_comment = 0;
|
||||
|
||||
return cf;
|
||||
}
|
||||
|
||||
void csv_close(CSVFILE *cf)
|
||||
{
|
||||
fclose(cf->f);
|
||||
csv_close_nu(cf);
|
||||
}
|
||||
|
||||
void csv_close_nu(CSVFILE *cf)
|
||||
{
|
||||
free(cf->columns);
|
||||
free(cf->buffer);
|
||||
memset(cf, 0, sizeof(*cf));
|
||||
free(cf);
|
||||
}
|
||||
|
||||
/**
|
||||
* internal helper function for the library.
|
||||
*/
|
||||
static int ensure_items_array(CSVFILE *cf)
|
||||
{
|
||||
if (cf->num_columns > MAX_NUM_COLUMNS) {
|
||||
return FAILURE;
|
||||
} else if (cf->num_columns < cf->alloc_column_ptrs) {
|
||||
return SUCCESS;
|
||||
} else {
|
||||
char **tmp;
|
||||
cf->alloc_column_ptrs = cf->num_columns * 2;
|
||||
tmp =
|
||||
realloc(cf->columns,
|
||||
cf->alloc_column_ptrs * sizeof(char *));
|
||||
if (!tmp) {
|
||||
return FAILURE;
|
||||
} else {
|
||||
cf->columns = tmp;
|
||||
return SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function checks if CSV file is a valid one.
|
||||
*/
|
||||
bool csv_is_valid(CSVFILE *cf)
|
||||
{
|
||||
if (!cf) {
|
||||
return false;
|
||||
} else if (!cf->f) {
|
||||
return false;
|
||||
} else if (!cf->columns) {
|
||||
return false;
|
||||
} else if (!cf->buffer) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static int csv_read_line(CSVFILE *cf)
|
||||
{
|
||||
char *line;
|
||||
char *c;
|
||||
int i, len;
|
||||
int already_read = 0;
|
||||
/* fgets reads at most buffer_size-1 characters and always places NULL
|
||||
* at the end. */
|
||||
|
||||
while (true) {
|
||||
line = fgets(cf->buffer + already_read,
|
||||
cf->buffer_size - already_read, cf->f);
|
||||
if (!line) {
|
||||
return FAILURE;
|
||||
}
|
||||
line = cf->buffer;
|
||||
/* check that entire line was read; if failed, expand buffer and retry
|
||||
* or (in case of eof) be happy with what we have */
|
||||
c = line;
|
||||
i = 0;
|
||||
|
||||
while (*c && *c != '\n') {
|
||||
c++;
|
||||
i++;
|
||||
}
|
||||
len = i;
|
||||
if (len > MAX_LINE_LENGTH) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
/* buffer ends with 0 while it is not an EOF - sign that we have NOT read entire line
|
||||
* - try to expand buffer*/
|
||||
if (!*c && !feof(cf->f)) {
|
||||
already_read = cf->buffer_size - 1;
|
||||
cf->buffer_size *= 2;
|
||||
char *tmp = realloc(cf->buffer, cf->buffer_size);
|
||||
|
||||
if (tmp) {
|
||||
cf->buffer = tmp;
|
||||
continue;
|
||||
} else {
|
||||
return FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (cf->buffer[i] == '\n') {
|
||||
cf->buffer[i] = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int csv_read(CSVFILE *cf)
|
||||
{
|
||||
int i, j, spaces_at_end;
|
||||
bool parsing_token = false; /* if false, "cursor" is over whitespace, otherwise
|
||||
* it is over part of token */
|
||||
|
||||
bool quotation = false;
|
||||
if (!csv_is_valid(cf)) {
|
||||
return FAILURE;
|
||||
}
|
||||
if (csv_read_line(cf)) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
cf->num_columns = 0;
|
||||
cf->columns[0] = 0;
|
||||
spaces_at_end = 0;
|
||||
|
||||
while (cf->buffer[i]) {
|
||||
if (quotation) { /* handling text within quotation marks -
|
||||
* ignore commas in this kind of text and don't strip spaces */
|
||||
if (cf->buffer[i] == '"' && cf->buffer[i + 1] == '"') {
|
||||
/* double quotation mark is considered escaped quotation by
|
||||
* Micros~1 Excel. We should do likewise */
|
||||
if (!parsing_token) { /* start of an cf->buffer */
|
||||
cf->columns[cf->num_columns] =
|
||||
&cf->buffer[i];
|
||||
parsing_token = true;
|
||||
}
|
||||
++i;
|
||||
memmove_s(cf->columns[cf->num_columns] + 1,
|
||||
cf->buffer_size - (cf->columns[cf->num_columns] - cf->buffer),
|
||||
cf->columns[cf->num_columns],
|
||||
&cf->buffer[i] - cf->columns[cf->num_columns]);
|
||||
cf->columns[cf->num_columns]++;
|
||||
} else if (cf->buffer[i] == '"') {
|
||||
quotation = false;
|
||||
parsing_token = false;
|
||||
cf->buffer[i] = 0;
|
||||
} else if (!parsing_token) { /* start of an cf->buffer */
|
||||
cf->columns[cf->num_columns] = &cf->buffer[i];
|
||||
parsing_token = true;
|
||||
}
|
||||
} else { /* handling text outside quotation mark */
|
||||
if (cf->buffer[i] == cf->csv_separator) {
|
||||
(cf->num_columns)++;
|
||||
if (ensure_items_array(cf)) {
|
||||
return FAILURE;
|
||||
}
|
||||
cf->columns[cf->num_columns] = 0;
|
||||
parsing_token = false;
|
||||
cf->buffer[i] = 0;
|
||||
for (j = i - spaces_at_end; j != i; ++j) {
|
||||
cf->buffer[j] = 0;
|
||||
}
|
||||
|
||||
} else if (cf->buffer[i] == '"') {
|
||||
quotation = true;
|
||||
spaces_at_end = 0;
|
||||
} else if (cf->csv_comment
|
||||
&& cf->buffer[i] == cf->csv_comment) {
|
||||
cf->buffer[i] = 0;
|
||||
break;
|
||||
} else if (!isspace(cf->buffer[i])) {
|
||||
if (!parsing_token) { /* start of an cf->buffer */
|
||||
if (!cf->columns[cf->num_columns]) {
|
||||
cf->columns[cf->num_columns] =
|
||||
&cf->buffer[i];
|
||||
}
|
||||
parsing_token = true;
|
||||
}
|
||||
spaces_at_end = 0;
|
||||
} else { /* no token.; clear spaces, possibly */
|
||||
parsing_token = false;
|
||||
spaces_at_end++;
|
||||
}
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
for (j = i - spaces_at_end; j != i; ++j) {
|
||||
cf->buffer[j] = 0;
|
||||
}
|
||||
|
||||
/*always consider empty line to have exactly one empty column */
|
||||
cf->num_columns++;
|
||||
|
||||
for (j = 0; j != cf->num_columns; ++j) {
|
||||
/* if no columns were detected during parse, make sure that columns[x]
|
||||
* points to an empty string and not into (NULL) */
|
||||
if (!cf->columns[j]) { /* so that empty columns will return empty string and
|
||||
not a null-pointer */
|
||||
cf->columns[j] = &cf->buffer[i];
|
||||
}
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
unsigned int csv_count_cols(CSVFILE *line)
|
||||
{
|
||||
return line->num_columns;
|
||||
}
|
||||
|
||||
int csv_empty_line(CSVFILE *cf)
|
||||
{
|
||||
if (!csv_is_valid(cf)) {
|
||||
return FAILURE;
|
||||
}
|
||||
if (0 == csv_count_cols(cf)) {
|
||||
return 1;
|
||||
} else if (1 == csv_count_cols(cf)) {
|
||||
const char *value = csv_get_col(cf, 0);
|
||||
if (strempty(value)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *csv_get_col(CSVFILE *cf, int coln)
|
||||
{
|
||||
if (!csv_is_valid(cf)) {
|
||||
return NULL;
|
||||
}
|
||||
return cf->columns[coln];
|
||||
}
|
||||
|
||||
char **csv_get_col_ptr(CSVFILE *cf)
|
||||
{
|
||||
return cf->columns;
|
||||
}
|
||||
|
||||
void csv_seek_beg(CSVFILE *cf)
|
||||
{
|
||||
fseek(cf->f, 0, SEEK_SET);
|
||||
}
|
||||
|
||||
int csv_feof(CSVFILE *cf)
|
||||
{
|
||||
return feof(cf->f);
|
||||
}
|
||||
|
||||
int csv_print(const char *path)
|
||||
{
|
||||
int i, j, k; /* column, line, row, within column */
|
||||
int num_col_lengths = DEF_ALLOC_COL_PTRS;
|
||||
static const int def_col_len = 5;
|
||||
int actual_num_cols = 1;
|
||||
|
||||
CSVFILE *cf = csv_open(path, "r");
|
||||
if (!cf) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
int *col_lengths = malloc(num_col_lengths * sizeof(int));
|
||||
if (!col_lengths) {
|
||||
csv_close(cf);
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
for (i = 0; i != num_col_lengths; ++i) {
|
||||
col_lengths[i] = def_col_len;
|
||||
}
|
||||
|
||||
/*calculate length of each column */
|
||||
i = j = 0;
|
||||
while (!csv_read(cf)) {
|
||||
int num_cols = csv_count_cols(cf);
|
||||
if (num_cols > actual_num_cols) {
|
||||
actual_num_cols = num_cols;
|
||||
}
|
||||
|
||||
if (num_cols > num_col_lengths) {
|
||||
/* CSV file happens to have more columns, than we have allocated
|
||||
* memory for */
|
||||
int *tmp =
|
||||
realloc(col_lengths, num_cols * 2 * sizeof(int));
|
||||
if (!tmp) {
|
||||
free(col_lengths);
|
||||
csv_close(cf);
|
||||
return FAILURE;
|
||||
}
|
||||
/* reallocation successful */
|
||||
col_lengths = tmp;
|
||||
for (i = num_col_lengths; i != num_cols * 2; ++i) {
|
||||
col_lengths[i] = def_col_len;
|
||||
}
|
||||
num_col_lengths = num_cols * 2;
|
||||
}
|
||||
|
||||
for (i = 0; i != csv_count_cols(cf); ++i) {
|
||||
int len = strnlen(csv_get_col(cf, i), MAX_STR_LEN);
|
||||
if (col_lengths[i] < len) {
|
||||
col_lengths[i] = len;
|
||||
}
|
||||
}
|
||||
++j;
|
||||
}
|
||||
|
||||
/*actually format pretty table */
|
||||
csv_seek_beg(cf);
|
||||
printf(" | ");
|
||||
|
||||
for (i = 0; i != actual_num_cols; ++i) {
|
||||
int before = col_lengths[i] / 2;
|
||||
|
||||
for (k = 0; k != before; ++k) {
|
||||
putchar(' ');
|
||||
}
|
||||
putchar(i + 'A');
|
||||
for (k = 0; k != col_lengths[i] - before - 1; ++k) {
|
||||
putchar(' ');
|
||||
}
|
||||
printf(" | ");
|
||||
}
|
||||
printf("\n-----|-");
|
||||
|
||||
for (i = 0; i != actual_num_cols; ++i) {
|
||||
for (k = 0; k != col_lengths[i]; ++k) {
|
||||
putchar('-');
|
||||
}
|
||||
printf("-|-");
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
j = 1;
|
||||
while (!csv_read(cf)) {
|
||||
printf("%4d | ", j);
|
||||
int num_cols = csv_count_cols(cf);
|
||||
for (i = 0; i != actual_num_cols; ++i) {
|
||||
if (i < num_cols) {
|
||||
char *c = csv_get_col(cf, i);
|
||||
for (k = 0; c[k]; k++) {
|
||||
putchar(c[k]);
|
||||
}
|
||||
} else {
|
||||
k = 0;
|
||||
}
|
||||
for (; k != col_lengths[i]; ++k) {
|
||||
putchar(' ');
|
||||
}
|
||||
printf(" | ");
|
||||
}
|
||||
++j;
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
free(col_lengths);
|
||||
csv_close(cf);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
#ifdef __CSV_SAMPLE__
|
||||
/**
|
||||
* usage example for csvparse library
|
||||
* gcc -ggdb csvparse.c -I../common -D__CSV_SAMPLE__ -ocsvsample
|
||||
*/
|
||||
int main()
|
||||
{
|
||||
puts("Validated configurations to run Intel CAS");
|
||||
csv_print("../../tools/build_installer/utils/validated_configurations.csv");
|
||||
putchar('\n');
|
||||
|
||||
puts("IO Classes for Intel CAS");
|
||||
csv_print("../../tools/build_installer/utils/default_ioclasses.csv");
|
||||
putchar('\n');
|
||||
|
||||
}
|
||||
#endif
|
103
casadm/csvparse.h
Normal file
103
casadm/csvparse.h
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef __CSVPARSE_H_
|
||||
#define __CSVPARSE_H_
|
||||
#include <stdio.h>
|
||||
/**
|
||||
* @file
|
||||
* @brief Generic CSV input/output library
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* data structure holding info about CSV file being read.
|
||||
* @note there is no need to directly manipulate any field of this structure.
|
||||
* Csvparse library handles everything.
|
||||
*/
|
||||
struct CSVFILE_t;
|
||||
|
||||
/**
|
||||
* This is to mimic semantics of stdio FILE*, which also is a typedef for a structure.
|
||||
*/
|
||||
typedef struct CSVFILE_t CSVFILE;
|
||||
|
||||
|
||||
CSVFILE *csv_open(const char *path, const char *mode);
|
||||
|
||||
CSVFILE *csv_fopen(FILE *f);
|
||||
CSVFILE *csv_fdopen(int fd);
|
||||
|
||||
|
||||
/**
|
||||
* close csv file. this is a direct counterpart to csv_open
|
||||
*/
|
||||
void csv_close(CSVFILE *cf);
|
||||
|
||||
/**
|
||||
* close a csv without closing underlying plain fle object (so that all
|
||||
* structures allocated by csv parsere are freed but syscall close(2) isn't issued
|
||||
* - this is designed as counterpart to csv_fopen or csv_fdopen
|
||||
*/
|
||||
void csv_close_nu(CSVFILE *cf);
|
||||
|
||||
/**
|
||||
* @param cf csv file handle to read
|
||||
*
|
||||
* Read line from CSV file; return 0 if line was successfully read
|
||||
* return nonzero if eof or error was observed
|
||||
* Error may mean end of file or i.e. memory allocation error for temporary buffers
|
||||
*/
|
||||
int csv_read(CSVFILE *cf);
|
||||
|
||||
/**
|
||||
* @return true if end of file occured.
|
||||
*/
|
||||
int csv_feof(CSVFILE *cf);
|
||||
|
||||
/**
|
||||
* return number of columns
|
||||
* @return # of columns in a csv file
|
||||
*/
|
||||
unsigned int csv_count_cols(CSVFILE *line);
|
||||
|
||||
/**
|
||||
* return given column of recently read row
|
||||
* @param coln - column number
|
||||
* @return pointer to field of csv file as a string; no range checking is performed,
|
||||
* so if coln given exceeds actual number of columns defined in this row, error will occur
|
||||
*/
|
||||
char* csv_get_col(CSVFILE *cf, int coln);
|
||||
|
||||
/**
|
||||
* return entire row as a set of pointers to individual columns (unchecked function
|
||||
* returns internal representation. state is guaranteed to be correct only when
|
||||
* csv_read returned success;
|
||||
*/
|
||||
char** csv_get_col_ptr(CSVFILE *cf);
|
||||
|
||||
/**
|
||||
* Check if current line is empty
|
||||
*
|
||||
* @param cf - CVS file instance
|
||||
* @retval 1 - empty line
|
||||
* @retval 0 - no empty line
|
||||
*/
|
||||
int csv_empty_line(CSVFILE *cf);
|
||||
|
||||
/**
|
||||
* Seek to the begining of CSV file; this allows reading file again, from the begining
|
||||
*/
|
||||
void csv_seek_beg(CSVFILE *cf);
|
||||
|
||||
/**
|
||||
* This function prints CVS file in human readable format to the STD output
|
||||
*
|
||||
* @param path - Path to the CVS file
|
||||
* @return Operation status. 0 - Success, otherwise error during printing
|
||||
*/
|
||||
int csv_print(const char *path);
|
||||
|
||||
#endif
|
244
casadm/extended_err_msg.c
Normal file
244
casadm/extended_err_msg.c
Normal file
@ -0,0 +1,244 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "safeclib/safe_lib.h"
|
||||
#include <cas_ioctl_codes.h>
|
||||
#include "extended_err_msg.h"
|
||||
|
||||
struct {
|
||||
int cas_error;
|
||||
const char *msg;
|
||||
} static cas_error_code_map[] = {
|
||||
|
||||
/* IOC error mappings*/
|
||||
{
|
||||
OCF_ERR_INVAL,
|
||||
"Invalid input parameter"
|
||||
},
|
||||
{
|
||||
OCF_ERR_INVAL_VOLUME_TYPE,
|
||||
"Invalid volume type"
|
||||
},
|
||||
{
|
||||
OCF_ERR_INTR,
|
||||
"Interrupted by a signal"
|
||||
},
|
||||
{
|
||||
OCF_ERR_UNKNOWN,
|
||||
"Unknown error occurred"
|
||||
},
|
||||
{
|
||||
OCF_ERR_TOO_MANY_CACHES,
|
||||
"Too many caches"
|
||||
},
|
||||
{
|
||||
OCF_ERR_NO_MEM,
|
||||
"Not enough memory to allocate a new cache device"
|
||||
},
|
||||
{
|
||||
OCF_ERR_NO_FREE_RAM,
|
||||
"Not enough free RAM for cache metadata to start cache"
|
||||
},
|
||||
{
|
||||
OCF_ERR_START_CACHE_FAIL,
|
||||
"Failed to insert cache"
|
||||
},
|
||||
{
|
||||
OCF_ERR_CACHE_IN_USE,
|
||||
"At least one cas device is still in use"
|
||||
},
|
||||
{
|
||||
OCF_ERR_CACHE_NOT_EXIST,
|
||||
"Cache ID does not exist"
|
||||
},
|
||||
{
|
||||
OCF_ERR_CACHE_EXIST,
|
||||
"Cache ID already exists"
|
||||
},
|
||||
{
|
||||
OCF_ERR_TOO_MANY_CORES,
|
||||
"Too many core devices in cache"
|
||||
},
|
||||
{
|
||||
OCF_ERR_CORE_NOT_AVAIL,
|
||||
"Core device not available"
|
||||
},
|
||||
{
|
||||
OCF_ERR_CACHE_NOT_AVAIL,
|
||||
"Cache device not available"
|
||||
},
|
||||
{
|
||||
OCF_ERR_IO_CLASS_NOT_EXIST,
|
||||
"No such IO class ID in the cache"
|
||||
},
|
||||
{
|
||||
OCF_ERR_WRITE_CACHE,
|
||||
"Error while writing to cache device"
|
||||
},
|
||||
{
|
||||
OCF_ERR_WRITE_CORE,
|
||||
"Error while writing to core device"
|
||||
},
|
||||
{
|
||||
OCF_ERR_DIRTY_SHUTDOWN,
|
||||
"Please use --load option to restore previous cache state "
|
||||
"(Warning: data corruption may happen)\nOr initialize your "
|
||||
"cache using --force option. Warning: All dirty data will be "
|
||||
"lost!\n"
|
||||
},
|
||||
{
|
||||
OCF_ERR_DIRTY_EXISTS,
|
||||
"Cache closed with dirty data.\nPlease start cache using "
|
||||
"--load or --force option.\n"
|
||||
},
|
||||
{
|
||||
OCF_ERR_FLUSHING_INTERRUPTED,
|
||||
"Flushing of core interrupted"
|
||||
},
|
||||
{
|
||||
OCF_ERR_CANNOT_ADD_CORE_TO_POOL,
|
||||
"Error occurred during adding core device to core pool"
|
||||
},
|
||||
{
|
||||
OCF_ERR_CACHE_IN_INCOMPLETE_STATE,
|
||||
"Cache is in incomplete state - at least one core is inactive"
|
||||
},
|
||||
{
|
||||
OCF_ERR_CORE_IN_INACTIVE_STATE,
|
||||
"Core device is in inactive state"
|
||||
},
|
||||
{
|
||||
OCF_ERR_NOT_OPEN_EXC,
|
||||
"Cannot open device exclusively"
|
||||
},
|
||||
|
||||
/* CAS kernel error mappings*/
|
||||
{
|
||||
KCAS_ERR_ROOT,
|
||||
"Must be root"
|
||||
},
|
||||
{
|
||||
KCAS_ERR_SYSTEM,
|
||||
"System Error"
|
||||
},
|
||||
{
|
||||
KCAS_ERR_BAD_RANGE,
|
||||
"Range parameters are invalid"
|
||||
},
|
||||
{
|
||||
KCAS_ERR_DEV_SPACE,
|
||||
"Illegal range, out of device space"
|
||||
},
|
||||
{
|
||||
KCAS_ERR_INV_IOCTL,
|
||||
"Invalid ioctl"
|
||||
},
|
||||
{
|
||||
KCAS_ERR_DEV_PENDING,
|
||||
"Device opens or mount are pending to this cache"
|
||||
},
|
||||
{
|
||||
KCAS_ERR_DIRTY_EXISTS_NVME,
|
||||
"Cache device contains dirty data.\nIf you want to format it, "
|
||||
"please use --force option.\nWarning: all data will be lost!"
|
||||
},
|
||||
{
|
||||
KCAS_ERR_FILE_EXISTS,
|
||||
"Could not create exported object because file in /dev "
|
||||
"directory exists"
|
||||
},
|
||||
{
|
||||
KCAS_ERR_IN_UPGRADE,
|
||||
"Operation not allowed. CAS is in upgrade state"
|
||||
},
|
||||
{
|
||||
KCAS_ERR_UNALIGNED,
|
||||
"Cache device logical sector size is greater than core device "
|
||||
"logical sector size.\nConsider changing logical sector size "
|
||||
"on current cache device \nor try other device with the same "
|
||||
"logical sector size as core device."
|
||||
},
|
||||
{
|
||||
KCAS_ERR_NO_STORED_CONF,
|
||||
"Internal kernel module error" },
|
||||
{
|
||||
KCAS_ERR_ROLLBACK,
|
||||
"Cannot restore previous configuration"
|
||||
},
|
||||
{
|
||||
KCAS_ERR_NOT_NVME,
|
||||
"Given block device is not NVMe"
|
||||
},
|
||||
{
|
||||
KCAS_ERR_FORMAT_FAILED,
|
||||
"Failed to format NVMe device"
|
||||
},
|
||||
{
|
||||
KCAS_ERR_NVME_BAD_FORMAT,
|
||||
"NVMe is formatted to unsupported format"
|
||||
},
|
||||
{
|
||||
KCAS_ERR_CONTAINS_PART,
|
||||
"Device contains partitions.\nIf you want to continue, "
|
||||
"please use --force option.\nWarning: all data will be lost!"
|
||||
},
|
||||
{
|
||||
KCAS_ERR_A_PART,
|
||||
"Formatting of partition is unsupported."
|
||||
},
|
||||
{
|
||||
KCAS_ERR_REMOVED_DIRTY,
|
||||
"Flush error occured. Core has been set to detached state.\n"
|
||||
"Warning: Core device may contain inconsistent data.\n"
|
||||
"To access your data please add core back to the cache."
|
||||
},
|
||||
{
|
||||
KCAS_ERR_STOPPED_DIRTY,
|
||||
"Cache has been stopped with flushing error.\n"
|
||||
"Warning: Core devices may contain inconsistent data.\n"
|
||||
"To access your data, please start cache with --load option."
|
||||
},
|
||||
{
|
||||
KCAS_ERR_NO_CACHE_ATTACHED,
|
||||
"Operation not allowed. Caching device is not attached."
|
||||
},
|
||||
{
|
||||
KCAS_ERR_CORE_POOL_NOT_EMPTY,
|
||||
"Operation not allowed. Core pool is not empty."
|
||||
},
|
||||
{
|
||||
KCAS_ERR_CLS_RULE_UNKNOWN_CONDITION,
|
||||
"Unexpected classification rule condition"
|
||||
},
|
||||
{
|
||||
KCAS_ERR_CLS_RULE_INVALID_SYNTAX,
|
||||
"Invalid classification rule syntax"
|
||||
},
|
||||
|
||||
|
||||
};
|
||||
|
||||
const char *cas_strerr(int cas_error_code)
|
||||
{
|
||||
int i;
|
||||
int count = sizeof(cas_error_code_map) / sizeof(cas_error_code_map[0]);
|
||||
|
||||
if (cas_error_code == 0)
|
||||
return NULL; /* No Error */
|
||||
|
||||
cas_error_code = abs(cas_error_code);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (cas_error_code_map[i].cas_error == cas_error_code)
|
||||
return cas_error_code_map[i].msg;
|
||||
}
|
||||
|
||||
return strerror(cas_error_code);
|
||||
}
|
||||
|
6
casadm/extended_err_msg.h
Normal file
6
casadm/extended_err_msg.h
Normal file
@ -0,0 +1,6 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
const char *cas_strerr(int cas_error_code);
|
110
casadm/intvector.c
Normal file
110
casadm/intvector.c
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "intvector.h"
|
||||
|
||||
#define DEFAULT_CAPACITY 11
|
||||
|
||||
|
||||
struct intvector *vector_alloc()
|
||||
{
|
||||
struct intvector *v = malloc(sizeof(struct intvector));
|
||||
if (!v) {
|
||||
return 0;
|
||||
}
|
||||
if (vector_alloc_placement(v)) {
|
||||
free(v);
|
||||
return 0;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
int vector_alloc_placement(struct intvector *v)
|
||||
{
|
||||
v->content = malloc(sizeof(int) * DEFAULT_CAPACITY);
|
||||
if (!v->content) {
|
||||
return 1;
|
||||
}
|
||||
v->size = 0;
|
||||
v->capacity = DEFAULT_CAPACITY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vector_reserve(struct intvector *v, int s)
|
||||
{
|
||||
if (s < DEFAULT_CAPACITY || s < v->capacity) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *tmp = realloc(v->content, s*sizeof(int));
|
||||
if (!tmp) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
v->content = tmp;
|
||||
v->capacity = s;
|
||||
return 0;
|
||||
}
|
||||
void vector_free_placement(struct intvector *v)
|
||||
{
|
||||
free(v->content);
|
||||
}
|
||||
|
||||
void vector_free(struct intvector *v)
|
||||
{
|
||||
vector_free_placement(v);
|
||||
free(v);
|
||||
}
|
||||
|
||||
int vector_get(struct intvector *v, int i)
|
||||
{
|
||||
return v->content[i];
|
||||
}
|
||||
|
||||
int vector_set(struct intvector *v, int i, int x)
|
||||
{
|
||||
v->content[i]=x;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vector_zero(struct intvector *v)
|
||||
{
|
||||
memset(v->content, 0, sizeof(int) * v->size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vector_push_back(struct intvector *v, int x)
|
||||
{
|
||||
if (vector_capacity(v) == vector_size(v)) {
|
||||
if (vector_reserve(v, v->size*2)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
vector_set(v, v->size, x);
|
||||
v->size++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vector_size(struct intvector *v)
|
||||
{
|
||||
return v->size;
|
||||
}
|
||||
|
||||
int vector_capacity(struct intvector *v)
|
||||
{
|
||||
return v->capacity;
|
||||
}
|
||||
|
||||
int vector_resize(struct intvector *v, int s)
|
||||
{
|
||||
if (vector_reserve(v, s)) {
|
||||
return 1;
|
||||
}
|
||||
v->size = s;
|
||||
return 0;
|
||||
}
|
40
casadm/intvector.h
Normal file
40
casadm/intvector.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef __INTVECTOR_H
|
||||
#define __INTVECTOR_H
|
||||
struct intvector
|
||||
{
|
||||
int capacity;
|
||||
int size;
|
||||
int *content;
|
||||
};
|
||||
/* names of these functions (mostly) correspond to std::vector */
|
||||
|
||||
struct intvector *vector_alloc();
|
||||
|
||||
int vector_alloc_placement(struct intvector *v);
|
||||
|
||||
int vector_reserve(struct intvector *v, int s);
|
||||
|
||||
void vector_free(struct intvector *v);
|
||||
|
||||
void vector_free_placement(struct intvector *v);
|
||||
|
||||
int vector_get(struct intvector *v, int i);
|
||||
|
||||
int vector_set(struct intvector *v, int i, int x);
|
||||
|
||||
int vector_zero(struct intvector *v);
|
||||
|
||||
int vector_push_back(struct intvector *v, int x);
|
||||
|
||||
int vector_size(struct intvector *v);
|
||||
|
||||
int vector_capacity(struct intvector *v);
|
||||
|
||||
int vector_resize(struct intvector *v, int s);
|
||||
|
||||
#endif
|
27
casadm/ocf_env.h
Normal file
27
casadm/ocf_env.h
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef OCF_ENV_H_
|
||||
#define OCF_ENV_H_
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "safeclib/safe_lib.h"
|
||||
|
||||
#define min(x, y) ({ x < y ? x : y; })
|
||||
|
||||
#define ENV_BUG_ON(cond) ({ if (cond) exit(1); })
|
||||
|
||||
/* *** STRING OPERATIONS *** */
|
||||
|
||||
#define env_memset memset_s
|
||||
#define env_memcpy memcpy_s
|
||||
#define env_memcmp memcmp_s
|
||||
|
||||
#define env_strnlen strnlen_s
|
||||
#define env_strncmp strncmp
|
||||
#define env_strncpy strncpy_s
|
||||
|
||||
#endif /* OCF_ENV_H_ */
|
22
casadm/ocf_env_headers.h
Normal file
22
casadm/ocf_env_headers.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef __OCF_ENV_HEADERS_H__
|
||||
#define __OCF_ENV_HEADERS_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* TODO: Move prefix printing to context logger. */
|
||||
#define OCF_LOGO "CAS"
|
||||
#define OCF_PREFIX_SHORT "[" OCF_LOGO "] "
|
||||
#define OCF_PREFIX_LONG "Cache Acceleration Software Linux"
|
||||
|
||||
#define OCF_VERSION_MAIN CAS_VERSION_MAIN
|
||||
#define OCF_VERSION_MAJOR CAS_VERSION_MAJOR
|
||||
#define OCF_VERSION_MINOR CAS_VERSION_MINOR
|
||||
|
||||
#endif /* __OCF_ENV_HEADERS_H__ */
|
198
casadm/psort.c
Normal file
198
casadm/psort.c
Normal file
@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/timeb.h>
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include "safeclib/safe_mem_lib.h"
|
||||
#include "psort.h"
|
||||
|
||||
/**
|
||||
* internal state of parallel sort algorithm (entire task for main thread,
|
||||
* subtask for children).
|
||||
*/
|
||||
struct psort_state
|
||||
{
|
||||
void *defbase; /*!< base of master buffer sort
|
||||
* - needed to compute offsets*/
|
||||
int spawn_threads;
|
||||
void *base; /*!< base of subtask */
|
||||
size_t nmemb; /*!< number of members in subtask */
|
||||
size_t size; /*!< of each member */
|
||||
compar_t compar;
|
||||
int result; /*!< partial result */
|
||||
void *tmpbuf; /*!< temporary buffer for purpose of merge algorithm */
|
||||
};
|
||||
|
||||
void memcpy_replacement(void *dst, void *src, size_t size)
|
||||
{
|
||||
/**
|
||||
* Avoid this if possible. memcpy_s leads to crappy performance
|
||||
* defeating purpose of entire optimized sort.
|
||||
*/
|
||||
memcpy_s(dst, size, src, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* merge algorithm has O(N) spatial complexity and O(N) time complexity
|
||||
*/
|
||||
void merge_ranges(void *base, size_t nmemb1, size_t nmemb2, size_t size,
|
||||
compar_t compar, void *tmpbuf)
|
||||
{
|
||||
void *target_buf = tmpbuf;
|
||||
int i1, i2;
|
||||
|
||||
for (i1 = i2 = 0; i1 < nmemb1 || i2 < nmemb2;) {
|
||||
bool lil; /* lil means "left is less" */
|
||||
if (i1 == nmemb1) {
|
||||
lil = false;
|
||||
} else if (i2 ==nmemb2) {
|
||||
lil = true;
|
||||
} else if (compar(base + i1 * size,
|
||||
base + (nmemb1 +i2) * size) < 0) {
|
||||
lil = true;
|
||||
} else {
|
||||
lil = false;
|
||||
|
||||
}
|
||||
if (lil) {
|
||||
memcpy_replacement(target_buf + (i1 + i2) * size,
|
||||
base + i1 * size,
|
||||
size);
|
||||
i1++;
|
||||
} else {
|
||||
memcpy_replacement(target_buf + (i1 + i2) * size,
|
||||
base + (nmemb1 + i2) * size,
|
||||
size);
|
||||
i2++;
|
||||
}
|
||||
}
|
||||
memcpy_replacement(base, target_buf, (nmemb1 + nmemb2) * size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute quicksort on part or entirety of subrange. If subranges taken into
|
||||
* account, than merge partial sort results.
|
||||
*
|
||||
* Complexity | time | spatial
|
||||
* --------------------+-------------------+-----------
|
||||
* Quick Sort | O(n*lg(n)/ncpu) | O(1)
|
||||
* Merging | O(n) | O(N)
|
||||
* --------------------+-------------------+-----------
|
||||
* Entire algorithm | O(n+n*lg(n)/ncpu) | O(N)
|
||||
*
|
||||
* Effectively for suficiently large number of CPUs, sorting time
|
||||
* becomes linear to dataset:
|
||||
* \lim{ncpu \rightarrow \infty} O(n+\frac{n*lg(n)}{ncpu}) = O(n + 0^+) = O(n)
|
||||
* Less can't be achieved, as last merge can't be parallelized.
|
||||
*/
|
||||
void *psort_thread_fun(void *arg_v)
|
||||
{
|
||||
pthread_t thread;
|
||||
struct psort_state* arg = arg_v;
|
||||
struct psort_state base_state;
|
||||
struct psort_state child_state;
|
||||
memcpy_replacement(&base_state, arg, sizeof(base_state));
|
||||
if (arg->spawn_threads > 1) {
|
||||
/* local state (assume, input state is unmodifiable) */
|
||||
memcpy_replacement(&child_state, arg, sizeof(base_state));
|
||||
|
||||
base_state.spawn_threads /= 2;
|
||||
child_state.spawn_threads = arg->spawn_threads
|
||||
- base_state.spawn_threads;
|
||||
|
||||
base_state.nmemb /= 2;
|
||||
child_state.nmemb = arg->nmemb - base_state.nmemb;
|
||||
|
||||
child_state.base += base_state.size *
|
||||
base_state.nmemb;
|
||||
/* spawn child */
|
||||
if (pthread_create(&thread, 0, psort_thread_fun, &child_state)) {
|
||||
/* failed to create thread */
|
||||
arg->result = -errno;
|
||||
return arg_v;
|
||||
}
|
||||
}
|
||||
|
||||
if (1 == base_state.spawn_threads) {
|
||||
qsort(base_state.base, base_state.nmemb,
|
||||
base_state.size, base_state.compar);
|
||||
} else {
|
||||
psort_thread_fun(&base_state);
|
||||
if (base_state.result) {
|
||||
arg->result = base_state.result;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg->spawn_threads > 1) {
|
||||
if (pthread_join(thread, 0)) {
|
||||
arg->result = -errno;
|
||||
return arg_v;
|
||||
}
|
||||
if (child_state.result) {
|
||||
arg->result = child_state.result;
|
||||
return arg_v;
|
||||
}
|
||||
if (!arg->result) {
|
||||
merge_ranges(arg->base, base_state.nmemb,
|
||||
child_state.nmemb, arg->size,
|
||||
arg->compar,
|
||||
arg->tmpbuf + (base_state.base
|
||||
- base_state.defbase));
|
||||
}
|
||||
}
|
||||
return arg_v;
|
||||
}
|
||||
|
||||
/**
|
||||
* actual parallel sorting entry point
|
||||
*/
|
||||
int psort_main(void *base, size_t nmemb, size_t size,
|
||||
compar_t compar, int ncpu)
|
||||
{
|
||||
struct psort_state base_state;
|
||||
/* use half the number of logical CPUs for purpose of sorting */
|
||||
base_state.spawn_threads = ncpu;
|
||||
/* current num of CPUs */
|
||||
base_state.defbase = base;
|
||||
base_state.base = base;
|
||||
base_state.nmemb = nmemb;
|
||||
base_state.size = size;
|
||||
base_state.compar = compar;
|
||||
base_state.tmpbuf = malloc(size * nmemb);
|
||||
base_state.result = 0;
|
||||
if (!base_state.tmpbuf) {
|
||||
return -1;
|
||||
}
|
||||
psort_thread_fun(&base_state);
|
||||
free(base_state.tmpbuf);
|
||||
return base_state.result;
|
||||
}
|
||||
|
||||
void psort(void *base, size_t nmemb, size_t size,
|
||||
compar_t compar)
|
||||
{
|
||||
/* entry point to psort */
|
||||
int ncpu = sysconf(_SC_NPROCESSORS_ONLN)/2;
|
||||
int maxncpu = nmemb / 1024;
|
||||
if (maxncpu < ncpu) {
|
||||
ncpu = maxncpu;
|
||||
}
|
||||
/* don't invoke actual psort when less than 2 threads are needed */
|
||||
if (ncpu < 2) {
|
||||
qsort(base, nmemb, size, compar);
|
||||
} else {
|
||||
if (psort_main(base, nmemb, size, compar, ncpu)) {
|
||||
/* if parallel sorting failed (i.e. due to failed thread
|
||||
* creation, fall back to single threaded operation */
|
||||
qsort(base, nmemb, size, compar);
|
||||
}
|
||||
}
|
||||
}
|
22
casadm/psort.h
Normal file
22
casadm/psort.h
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef PSORT_H
|
||||
#define PSORT_H
|
||||
typedef int (*compar_t)(const void *, const void *);
|
||||
|
||||
/**
|
||||
* function does exactly the same thing as qsort, except, that it sorts
|
||||
* using many CPU cores, not just one.
|
||||
*
|
||||
* number of CPU cores is configured as half of the number of online
|
||||
* CPUs in the system.
|
||||
*/
|
||||
void psort(void *base, size_t nmemb, size_t size,
|
||||
compar_t compar);
|
||||
|
||||
|
||||
#endif
|
||||
|
72
casadm/safeclib/ignore_handler_s.c
Normal file
72
casadm/safeclib/ignore_handler_s.c
Normal file
@ -0,0 +1,72 @@
|
||||
/*------------------------------------------------------------------
|
||||
* ignore_handler_s.c
|
||||
*
|
||||
* 2012, Jonathan Toppins <jtoppins@users.sourceforge.net>
|
||||
*
|
||||
* Copyright (c) 2012 Cisco Systems
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "safeclib_private.h"
|
||||
|
||||
/**
|
||||
* NAME
|
||||
* ignore_handler_s
|
||||
*
|
||||
* SYNOPSIS
|
||||
* #include "safe_lib.h"
|
||||
* void ignore_handler_s(const char *msg, void *ptr, errno_t error)
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function simply returns to the caller.
|
||||
*
|
||||
* SPECIFIED IN
|
||||
* ISO/IEC JTC1 SC22 WG14 N1172, Programming languages, environments
|
||||
* and system software interfaces, Extensions to the C Library,
|
||||
* Part I: Bounds-checking interfaces
|
||||
*
|
||||
* INPUT PARAMETERS
|
||||
* msg Pointer to the message describing the error
|
||||
*
|
||||
* ptr Pointer to aassociated data. Can be NULL.
|
||||
*
|
||||
* error The error code encountered.
|
||||
*
|
||||
* RETURN VALUE
|
||||
* Returns no value.
|
||||
*
|
||||
* ALSO SEE
|
||||
* abort_handler_s()
|
||||
*
|
||||
*/
|
||||
|
||||
void ignore_handler_s(const char *msg, void *ptr, errno_t error)
|
||||
{
|
||||
|
||||
sldebug_printf("IGNORE CONSTRAINT HANDLER: (%u) %s\n", error,
|
||||
(msg) ? msg : "Null message");
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(ignore_handler_s);
|
853
casadm/safeclib/mem_primitives_lib.c
Normal file
853
casadm/safeclib/mem_primitives_lib.c
Normal file
@ -0,0 +1,853 @@
|
||||
/*------------------------------------------------------------------
|
||||
* mem_primitives_lib.c - Unguarded Memory Copy Routines
|
||||
*
|
||||
* February 2005, Bo Berry
|
||||
*
|
||||
* Copyright (c) 2005-2009 Cisco Systems
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "mem_primitives_lib.h"
|
||||
|
||||
/*
|
||||
* mem_primitives_lib.c provides unguarded memory routines
|
||||
* that are used by the safe_mem_library. These routines
|
||||
* may also be used by an application, but the application
|
||||
* is responsible for all parameter validation and alignment.
|
||||
*/
|
||||
|
||||
/**
|
||||
* NAME
|
||||
* mem_prim_set - Sets memory to value
|
||||
*
|
||||
* SYNOPSIS
|
||||
* #include "mem_primitives_lib.h"
|
||||
* void
|
||||
* mem_prim_set(void *dest, uint32_t len, uint8_t value)
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Sets len bytes starting at dest to the specified value
|
||||
*
|
||||
* INPUT PARAMETERS
|
||||
* dest - pointer to memory that will be set to value
|
||||
*
|
||||
* len - number of bytes to be set
|
||||
*
|
||||
* value - byte value
|
||||
*
|
||||
* OUTPUT PARAMETERS
|
||||
* dest - is updated
|
||||
*
|
||||
* RETURN VALUE
|
||||
* none
|
||||
*
|
||||
*/
|
||||
void
|
||||
mem_prim_set (void *dest, uint32_t len, uint8_t value)
|
||||
{
|
||||
uint8_t *dp;
|
||||
uint32_t count;
|
||||
uint32_t lcount;
|
||||
|
||||
uint32_t *lp;
|
||||
uint32_t value32;
|
||||
|
||||
count = len;
|
||||
|
||||
dp = dest;
|
||||
|
||||
value32 = value | (value << 8) | (value << 16) | (value << 24);
|
||||
|
||||
/*
|
||||
* First, do the few bytes to get uint32_t aligned.
|
||||
*/
|
||||
for (; count && ( (uintptr_t)dp & (sizeof(uint32_t)-1) ); count--) {
|
||||
*dp++ = value;
|
||||
}
|
||||
|
||||
/*
|
||||
* Then do the uint32_ts, unrolled the loop for performance
|
||||
*/
|
||||
lp = (uint32_t *)dp;
|
||||
lcount = count >> 2;
|
||||
|
||||
while (lcount != 0) {
|
||||
|
||||
switch (lcount) {
|
||||
/*
|
||||
* Here we do blocks of 8. Once the remaining count
|
||||
* drops below 8, take the fast track to finish up.
|
||||
*/
|
||||
default:
|
||||
*lp++ = value32; *lp++ = value32; *lp++ = value32; *lp++ = value32;
|
||||
*lp++ = value32; *lp++ = value32; *lp++ = value32; *lp++ = value32;
|
||||
*lp++ = value32; *lp++ = value32; *lp++ = value32; *lp++ = value32;
|
||||
*lp++ = value32; *lp++ = value32; *lp++ = value32; *lp++ = value32;
|
||||
lcount -= 16;
|
||||
break;
|
||||
|
||||
case 15: *lp++ = value32;
|
||||
case 14: *lp++ = value32;
|
||||
case 13: *lp++ = value32;
|
||||
case 12: *lp++ = value32;
|
||||
case 11: *lp++ = value32;
|
||||
case 10: *lp++ = value32;
|
||||
case 9: *lp++ = value32;
|
||||
case 8: *lp++ = value32;
|
||||
|
||||
case 7: *lp++ = value32;
|
||||
case 6: *lp++ = value32;
|
||||
case 5: *lp++ = value32;
|
||||
case 4: *lp++ = value32;
|
||||
case 3: *lp++ = value32;
|
||||
case 2: *lp++ = value32;
|
||||
case 1: *lp++ = value32;
|
||||
lcount = 0;
|
||||
break;
|
||||
}
|
||||
} /* end while */
|
||||
|
||||
|
||||
dp = (uint8_t *)lp;
|
||||
|
||||
/*
|
||||
* compute the number of remaining bytes
|
||||
*/
|
||||
count &= (sizeof(uint32_t)-1);
|
||||
|
||||
/*
|
||||
* remaining bytes
|
||||
*/
|
||||
for (; count; dp++, count--) {
|
||||
*dp = value;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* NAME
|
||||
* mem_prim_set16 - Sets memory to value
|
||||
*
|
||||
* SYNOPSIS
|
||||
* #include "mem_primitives_lib.h"
|
||||
* void
|
||||
* mem_prim_set16(uint16_t *dp, uint32_t len, uint16_t value)
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Sets len uint16_ts starting at dest to the specified value.
|
||||
* Pointers must meet system alignment requirements.
|
||||
*
|
||||
* INPUT PARAMETERS
|
||||
* dest - pointer to memory that will be set to value
|
||||
*
|
||||
* len - number of uint16_ts to be set
|
||||
*
|
||||
* value - uint16_t value
|
||||
*
|
||||
* OUTPUT PARAMETERS
|
||||
* dest - is updated
|
||||
*
|
||||
* RETURN VALUE
|
||||
* none
|
||||
*
|
||||
*/
|
||||
void
|
||||
mem_prim_set16 (uint16_t *dp, uint32_t len, uint16_t value)
|
||||
{
|
||||
|
||||
while (len != 0) {
|
||||
|
||||
switch (len) {
|
||||
/*
|
||||
* Here we do blocks of 8. Once the remaining count
|
||||
* drops below 8, take the fast track to finish up.
|
||||
*/
|
||||
default:
|
||||
*dp++ = value; *dp++ = value; *dp++ = value; *dp++ = value;
|
||||
*dp++ = value; *dp++ = value; *dp++ = value; *dp++ = value;
|
||||
*dp++ = value; *dp++ = value; *dp++ = value; *dp++ = value;
|
||||
*dp++ = value; *dp++ = value; *dp++ = value; *dp++ = value;
|
||||
len -= 16;
|
||||
break;
|
||||
|
||||
case 15: *dp++ = value;
|
||||
case 14: *dp++ = value;
|
||||
case 13: *dp++ = value;
|
||||
case 12: *dp++ = value;
|
||||
case 11: *dp++ = value;
|
||||
case 10: *dp++ = value;
|
||||
case 9: *dp++ = value;
|
||||
case 8: *dp++ = value;
|
||||
|
||||
case 7: *dp++ = value;
|
||||
case 6: *dp++ = value;
|
||||
case 5: *dp++ = value;
|
||||
case 4: *dp++ = value;
|
||||
case 3: *dp++ = value;
|
||||
case 2: *dp++ = value;
|
||||
case 1: *dp++ = value;
|
||||
len = 0;
|
||||
break;
|
||||
}
|
||||
} /* end while */
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* NAME
|
||||
* mem_prim_set32 - Sets memory to the uint32_t value
|
||||
*
|
||||
* SYNOPSIS
|
||||
* #include "mem_primitives_lib.h"
|
||||
* void
|
||||
* mem_prim_set32(uint32_t *dp, uint32_t len, uint32_t value)
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Sets len uint32_ts starting at dest to the specified value
|
||||
* Pointers must meet system alignment requirements.
|
||||
*
|
||||
* INPUT PARAMETERS
|
||||
* dest - pointer to memory that will be set to value
|
||||
*
|
||||
* len - number of uint32_ts to be set
|
||||
*
|
||||
* value - uint32_t value
|
||||
*
|
||||
* OUTPUT PARAMETERS
|
||||
* dest - is updated
|
||||
*
|
||||
* RETURN VALUE
|
||||
* none
|
||||
*
|
||||
*/
|
||||
void
|
||||
mem_prim_set32 (uint32_t *dp, uint32_t len, uint32_t value)
|
||||
{
|
||||
|
||||
while (len != 0) {
|
||||
|
||||
switch (len) {
|
||||
/*
|
||||
* Here we do blocks of 8. Once the remaining count
|
||||
* drops below 8, take the fast track to finish up.
|
||||
*/
|
||||
default:
|
||||
*dp++ = value; *dp++ = value; *dp++ = value; *dp++ = value;
|
||||
*dp++ = value; *dp++ = value; *dp++ = value; *dp++ = value;
|
||||
*dp++ = value; *dp++ = value; *dp++ = value; *dp++ = value;
|
||||
*dp++ = value; *dp++ = value; *dp++ = value; *dp++ = value;
|
||||
len -= 16;
|
||||
break;
|
||||
|
||||
case 15: *dp++ = value;
|
||||
case 14: *dp++ = value;
|
||||
case 13: *dp++ = value;
|
||||
case 12: *dp++ = value;
|
||||
case 11: *dp++ = value;
|
||||
case 10: *dp++ = value;
|
||||
case 9: *dp++ = value;
|
||||
case 8: *dp++ = value;
|
||||
|
||||
case 7: *dp++ = value;
|
||||
case 6: *dp++ = value;
|
||||
case 5: *dp++ = value;
|
||||
case 4: *dp++ = value;
|
||||
case 3: *dp++ = value;
|
||||
case 2: *dp++ = value;
|
||||
case 1: *dp++ = value;
|
||||
len = 0;
|
||||
break;
|
||||
}
|
||||
} /* end while */
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* NAME
|
||||
* mem_prim_move - Move (handles overlap) memory
|
||||
*
|
||||
* SYNOPSIS
|
||||
* #include "mem_primitives_lib.h"
|
||||
* void
|
||||
* mem_prim_move(void *dest, const void *src, uint32_t len)
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Moves at most slen bytes from src to dest, up to dmax
|
||||
* bytes. Dest may overlap with src.
|
||||
*
|
||||
* INPUT PARAMETERS
|
||||
* dest - pointer to the memory that will be replaced by src.
|
||||
*
|
||||
* src - pointer to the memory that will be copied
|
||||
* to dest
|
||||
*
|
||||
* len - maximum number bytes of src that can be copied
|
||||
*
|
||||
* OUTPUT PARAMETERS
|
||||
* dest - is updated
|
||||
*
|
||||
* RETURN VALUE
|
||||
* none
|
||||
*
|
||||
*/
|
||||
void
|
||||
mem_prim_move (void *dest, const void *src, uint32_t len)
|
||||
{
|
||||
|
||||
#define wsize sizeof(uint32_t)
|
||||
#define wmask (wsize - 1)
|
||||
|
||||
uint8_t *dp = dest;
|
||||
const uint8_t *sp = src;
|
||||
|
||||
uint32_t tsp;
|
||||
|
||||
/*
|
||||
* Determine if we need to copy forward or backward (overlap)
|
||||
*/
|
||||
if ((uintptr_t)dp < (uintptr_t)sp) {
|
||||
/*
|
||||
* Copy forward.
|
||||
*/
|
||||
|
||||
/*
|
||||
* get a working copy of src for bit operations
|
||||
*/
|
||||
tsp = (uintptr_t)sp;
|
||||
|
||||
/*
|
||||
* Try to align both operands. This cannot be done
|
||||
* unless the low bits match.
|
||||
*/
|
||||
if ((tsp | (uintptr_t)dp) & wmask) {
|
||||
/*
|
||||
* determine how many bytes to copy to align operands
|
||||
*/
|
||||
if ((tsp ^ (uintptr_t)dp) & wmask || len < wsize) {
|
||||
tsp = len;
|
||||
|
||||
} else {
|
||||
tsp = wsize - (tsp & wmask);
|
||||
}
|
||||
|
||||
len -= tsp;
|
||||
|
||||
/*
|
||||
* make the alignment
|
||||
*/
|
||||
do {
|
||||
*dp++ = *sp++;
|
||||
} while (--tsp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now copy, then mop up any trailing bytes.
|
||||
*/
|
||||
tsp = len / wsize;
|
||||
|
||||
if (tsp > 0) {
|
||||
|
||||
do {
|
||||
*(uint32_t *)dp = *(uint32_t *)sp;
|
||||
|
||||
sp += wsize;
|
||||
dp += wsize;
|
||||
} while (--tsp);
|
||||
}
|
||||
|
||||
/*
|
||||
* copy over the remaining bytes and we're done
|
||||
*/
|
||||
tsp = len & wmask;
|
||||
|
||||
if (tsp > 0) {
|
||||
do {
|
||||
*dp++ = *sp++;
|
||||
} while (--tsp);
|
||||
}
|
||||
|
||||
} else {
|
||||
/*
|
||||
* This section is used to copy backwards, to handle any
|
||||
* overlap. The alignment requires (tps&wmask) bytes to
|
||||
* align.
|
||||
*/
|
||||
|
||||
/*
|
||||
* go to end of the memory to copy
|
||||
*/
|
||||
sp += len;
|
||||
dp += len;
|
||||
|
||||
/*
|
||||
* get a working copy of src for bit operations
|
||||
*/
|
||||
tsp = (uintptr_t)sp;
|
||||
|
||||
/*
|
||||
* Try to align both operands.
|
||||
*/
|
||||
if ((tsp | (uintptr_t)dp) & wmask) {
|
||||
|
||||
if ((tsp ^ (uintptr_t)dp) & wmask || len <= wsize) {
|
||||
tsp = len;
|
||||
} else {
|
||||
tsp &= wmask;
|
||||
}
|
||||
|
||||
len -= tsp;
|
||||
|
||||
/*
|
||||
* make the alignment
|
||||
*/
|
||||
do {
|
||||
*--dp = *--sp;
|
||||
} while (--tsp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now copy in uint32_t units, then mop up any trailing bytes.
|
||||
*/
|
||||
tsp = len / wsize;
|
||||
|
||||
if (tsp > 0) {
|
||||
do {
|
||||
sp -= wsize;
|
||||
dp -= wsize;
|
||||
|
||||
*(uint32_t *)dp = *(uint32_t *)sp;
|
||||
} while (--tsp);
|
||||
}
|
||||
|
||||
/*
|
||||
* copy over the remaining bytes and we're done
|
||||
*/
|
||||
tsp = len & wmask;
|
||||
if (tsp > 0) {
|
||||
tsp = len & wmask;
|
||||
do {
|
||||
*--dp = *--sp;
|
||||
} while (--tsp);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* NAME
|
||||
* mem_prim_move8 - Move (handles overlap) memory
|
||||
*
|
||||
* SYNOPSIS
|
||||
* #include "mem_primitives_lib.h"
|
||||
* void
|
||||
* mem_prim_move8(void *dest, const void *src, uint32_t len)
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Moves at most len uint8_ts from sp to dp.
|
||||
* The destination may overlap with source.
|
||||
*
|
||||
* INPUT PARAMETERS
|
||||
* dp - pointer to the memory that will be replaced by sp.
|
||||
*
|
||||
* sp - pointer to the memory that will be copied
|
||||
* to dp
|
||||
*
|
||||
* len - maximum number uint8_t of sp that can be copied
|
||||
*
|
||||
* OUTPUT PARAMETERS
|
||||
* dp - pointer to the memory that will be replaced by sp.
|
||||
*
|
||||
* RETURN VALUE
|
||||
* none
|
||||
*
|
||||
*/
|
||||
void
|
||||
mem_prim_move8 (uint8_t *dp, const uint8_t *sp, uint32_t len)
|
||||
{
|
||||
|
||||
/*
|
||||
* Determine if we need to copy forward or backward (overlap)
|
||||
*/
|
||||
if (dp < sp) {
|
||||
/*
|
||||
* Copy forward.
|
||||
*/
|
||||
|
||||
while (len != 0) {
|
||||
|
||||
switch (len) {
|
||||
/*
|
||||
* Here we do blocks of 8. Once the remaining count
|
||||
* drops below 8, take the fast track to finish up.
|
||||
*/
|
||||
default:
|
||||
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
|
||||
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
|
||||
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
|
||||
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
|
||||
len -= 16;
|
||||
break;
|
||||
|
||||
case 15: *dp++ = *sp++;
|
||||
case 14: *dp++ = *sp++;
|
||||
case 13: *dp++ = *sp++;
|
||||
case 12: *dp++ = *sp++;
|
||||
case 11: *dp++ = *sp++;
|
||||
case 10: *dp++ = *sp++;
|
||||
case 9: *dp++ = *sp++;
|
||||
case 8: *dp++ = *sp++;
|
||||
|
||||
case 7: *dp++ = *sp++;
|
||||
case 6: *dp++ = *sp++;
|
||||
case 5: *dp++ = *sp++;
|
||||
case 4: *dp++ = *sp++;
|
||||
case 3: *dp++ = *sp++;
|
||||
case 2: *dp++ = *sp++;
|
||||
case 1: *dp++ = *sp++;
|
||||
len = 0;
|
||||
break;
|
||||
}
|
||||
} /* end while */
|
||||
|
||||
} else {
|
||||
/*
|
||||
* This section is used to copy backwards, to handle any
|
||||
* overlap. The alignment requires (tps&wmask) bytes to
|
||||
* align.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* go to end of the memory to copy
|
||||
*/
|
||||
sp += len;
|
||||
dp += len;
|
||||
|
||||
while (len != 0) {
|
||||
|
||||
switch (len) {
|
||||
/*
|
||||
* Here we do blocks of 8. Once the remaining count
|
||||
* drops below 8, take the fast track to finish up.
|
||||
*/
|
||||
default:
|
||||
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
|
||||
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
|
||||
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
|
||||
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
|
||||
len -= 16;
|
||||
break;
|
||||
|
||||
case 15: *--dp = *--sp;
|
||||
case 14: *--dp = *--sp;
|
||||
case 13: *--dp = *--sp;
|
||||
case 12: *--dp = *--sp;
|
||||
case 11: *--dp = *--sp;
|
||||
case 10: *--dp = *--sp;
|
||||
case 9: *--dp = *--sp;
|
||||
case 8: *--dp = *--sp;
|
||||
|
||||
case 7: *--dp = *--sp;
|
||||
case 6: *--dp = *--sp;
|
||||
case 5: *--dp = *--sp;
|
||||
case 4: *--dp = *--sp;
|
||||
case 3: *--dp = *--sp;
|
||||
case 2: *--dp = *--sp;
|
||||
case 1: *--dp = *--sp;
|
||||
len = 0;
|
||||
break;
|
||||
}
|
||||
} /* end while */
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* NAME
|
||||
* mem_prim_move16 - Move (handles overlap) memory
|
||||
*
|
||||
* SYNOPSIS
|
||||
* #include "mem_primitives_lib.h"
|
||||
* void
|
||||
* mem_prim_move16(void *dest, const void *src, uint32_t len)
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Moves at most len uint16_ts from sp to dp.
|
||||
* The destination may overlap with source.
|
||||
*
|
||||
* INPUT PARAMETERS
|
||||
* dp - pointer to the memory that will be replaced by sp.
|
||||
*
|
||||
* sp - pointer to the memory that will be copied
|
||||
* to dp
|
||||
*
|
||||
* len - maximum number uint16_t of sp that can be copied
|
||||
*
|
||||
* OUTPUT PARAMETERS
|
||||
* dp - is updated
|
||||
*
|
||||
* RETURN VALUE
|
||||
* none
|
||||
*
|
||||
*/
|
||||
void
|
||||
mem_prim_move16 (uint16_t *dp, const uint16_t *sp, uint32_t len)
|
||||
{
|
||||
|
||||
/*
|
||||
* Determine if we need to copy forward or backward (overlap)
|
||||
*/
|
||||
if (dp < sp) {
|
||||
/*
|
||||
* Copy forward.
|
||||
*/
|
||||
|
||||
while (len != 0) {
|
||||
|
||||
switch (len) {
|
||||
/*
|
||||
* Here we do blocks of 8. Once the remaining count
|
||||
* drops below 8, take the fast track to finish up.
|
||||
*/
|
||||
default:
|
||||
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
|
||||
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
|
||||
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
|
||||
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
|
||||
len -= 16;
|
||||
break;
|
||||
|
||||
case 15: *dp++ = *sp++;
|
||||
case 14: *dp++ = *sp++;
|
||||
case 13: *dp++ = *sp++;
|
||||
case 12: *dp++ = *sp++;
|
||||
case 11: *dp++ = *sp++;
|
||||
case 10: *dp++ = *sp++;
|
||||
case 9: *dp++ = *sp++;
|
||||
case 8: *dp++ = *sp++;
|
||||
|
||||
case 7: *dp++ = *sp++;
|
||||
case 6: *dp++ = *sp++;
|
||||
case 5: *dp++ = *sp++;
|
||||
case 4: *dp++ = *sp++;
|
||||
case 3: *dp++ = *sp++;
|
||||
case 2: *dp++ = *sp++;
|
||||
case 1: *dp++ = *sp++;
|
||||
len = 0;
|
||||
break;
|
||||
}
|
||||
} /* end while */
|
||||
|
||||
} else {
|
||||
/*
|
||||
* This section is used to copy backwards, to handle any
|
||||
* overlap. The alignment requires (tps&wmask) bytes to
|
||||
* align.
|
||||
*/
|
||||
|
||||
/*
|
||||
* go to end of the memory to copy
|
||||
*/
|
||||
sp += len;
|
||||
dp += len;
|
||||
|
||||
while (len != 0) {
|
||||
|
||||
switch (len) {
|
||||
/*
|
||||
* Here we do blocks of 8. Once the remaining count
|
||||
* drops below 8, take the fast track to finish up.
|
||||
*/
|
||||
default:
|
||||
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
|
||||
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
|
||||
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
|
||||
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
|
||||
len -= 16;
|
||||
break;
|
||||
|
||||
case 15: *--dp = *--sp;
|
||||
case 14: *--dp = *--sp;
|
||||
case 13: *--dp = *--sp;
|
||||
case 12: *--dp = *--sp;
|
||||
case 11: *--dp = *--sp;
|
||||
case 10: *--dp = *--sp;
|
||||
case 9: *--dp = *--sp;
|
||||
case 8: *--dp = *--sp;
|
||||
|
||||
case 7: *--dp = *--sp;
|
||||
case 6: *--dp = *--sp;
|
||||
case 5: *--dp = *--sp;
|
||||
case 4: *--dp = *--sp;
|
||||
case 3: *--dp = *--sp;
|
||||
case 2: *--dp = *--sp;
|
||||
case 1: *--dp = *--sp;
|
||||
len = 0;
|
||||
break;
|
||||
}
|
||||
} /* end while */
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* NAME
|
||||
* mem_prim_move32 - Move (handles overlap) memory
|
||||
*
|
||||
* SYNOPSIS
|
||||
* #include "mem_primitives_lib.h"
|
||||
* void
|
||||
* mem_prim_move32(void *dest, const void *src, uint32_t len)
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Moves at most len uint32_ts from sp to dp.
|
||||
* The destination may overlap with source.
|
||||
*
|
||||
* INPUT PARAMETERS
|
||||
* dp - pointer to the memory that will be replaced by sp.
|
||||
*
|
||||
* sp - pointer to the memory that will be copied
|
||||
* to dp
|
||||
*
|
||||
* len - maximum number uint32_t of sp that can be copied
|
||||
*
|
||||
* OUTPUT PARAMETERS
|
||||
* dp - is updated
|
||||
*
|
||||
* RETURN VALUE
|
||||
* none
|
||||
*
|
||||
*/
|
||||
void
|
||||
mem_prim_move32 (uint32_t *dp, const uint32_t *sp, uint32_t len)
|
||||
{
|
||||
|
||||
/*
|
||||
* Determine if we need to copy forward or backward (overlap)
|
||||
*/
|
||||
if (dp < sp) {
|
||||
/*
|
||||
* Copy forward.
|
||||
*/
|
||||
|
||||
while (len != 0) {
|
||||
|
||||
switch (len) {
|
||||
/*
|
||||
* Here we do blocks of 8. Once the remaining count
|
||||
* drops below 8, take the fast track to finish up.
|
||||
*/
|
||||
default:
|
||||
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
|
||||
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
|
||||
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
|
||||
*dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++;
|
||||
len -= 16;
|
||||
break;
|
||||
|
||||
case 15: *dp++ = *sp++;
|
||||
case 14: *dp++ = *sp++;
|
||||
case 13: *dp++ = *sp++;
|
||||
case 12: *dp++ = *sp++;
|
||||
case 11: *dp++ = *sp++;
|
||||
case 10: *dp++ = *sp++;
|
||||
case 9: *dp++ = *sp++;
|
||||
case 8: *dp++ = *sp++;
|
||||
|
||||
case 7: *dp++ = *sp++;
|
||||
case 6: *dp++ = *sp++;
|
||||
case 5: *dp++ = *sp++;
|
||||
case 4: *dp++ = *sp++;
|
||||
case 3: *dp++ = *sp++;
|
||||
case 2: *dp++ = *sp++;
|
||||
case 1: *dp++ = *sp++;
|
||||
len = 0;
|
||||
break;
|
||||
}
|
||||
} /* end while */
|
||||
|
||||
} else {
|
||||
/*
|
||||
* This section is used to copy backwards, to handle any
|
||||
* overlap.
|
||||
*/
|
||||
|
||||
/*
|
||||
* go to end of the memory to copy
|
||||
*/
|
||||
sp += len;
|
||||
dp += len;
|
||||
|
||||
while (len != 0) {
|
||||
|
||||
switch (len) {
|
||||
/*
|
||||
* Here we do blocks of 8. Once the remaining count
|
||||
* drops below 8, take the fast track to finish up.
|
||||
*/
|
||||
default:
|
||||
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
|
||||
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
|
||||
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
|
||||
*--dp = *--sp; *--dp = *--sp; *--dp = *--sp; *--dp = *--sp;
|
||||
len -= 16;
|
||||
break;
|
||||
|
||||
case 15: *--dp = *--sp;
|
||||
case 14: *--dp = *--sp;
|
||||
case 13: *--dp = *--sp;
|
||||
case 12: *--dp = *--sp;
|
||||
case 11: *--dp = *--sp;
|
||||
case 10: *--dp = *--sp;
|
||||
case 9: *--dp = *--sp;
|
||||
case 8: *--dp = *--sp;
|
||||
|
||||
case 7: *--dp = *--sp;
|
||||
case 6: *--dp = *--sp;
|
||||
case 5: *--dp = *--sp;
|
||||
case 4: *--dp = *--sp;
|
||||
case 3: *--dp = *--sp;
|
||||
case 2: *--dp = *--sp;
|
||||
case 1: *--dp = *--sp;
|
||||
len = 0;
|
||||
break;
|
||||
}
|
||||
} /* end while */
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
74
casadm/safeclib/mem_primitives_lib.h
Normal file
74
casadm/safeclib/mem_primitives_lib.h
Normal file
@ -0,0 +1,74 @@
|
||||
/*------------------------------------------------------------------
|
||||
* mem_primitives_lib.h - Unguarded Memory Copy Routines
|
||||
*
|
||||
* October 2008, Bo Berry
|
||||
*
|
||||
* Copyright (c) 2008-2011 by Cisco Systems, Inc
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef __MEM_PRIMITIVES_LIB_H__
|
||||
#define __MEM_PRIMITIVES_LIB_H__
|
||||
|
||||
#include "safeclib_private.h"
|
||||
|
||||
/*
|
||||
* These are prototypes for _unguarded_ memory routines. The caller must
|
||||
* validate all parameters prior to invocation. Useful for diagnostics
|
||||
* and system initialization processing.
|
||||
*/
|
||||
|
||||
/* moves (handles overlap) memory */
|
||||
extern void
|
||||
mem_prim_move(void *dest, const void *src, uint32_t length);
|
||||
|
||||
|
||||
/* uint8_t moves (handles overlap) memory */
|
||||
extern void
|
||||
mem_prim_move8(uint8_t *dest, const uint8_t *src, uint32_t length);
|
||||
|
||||
/* uint16_t moves (handles overlap) memory */
|
||||
extern void
|
||||
mem_prim_move16(uint16_t *dest, const uint16_t *src, uint32_t length);
|
||||
|
||||
/* uint32_t moves (handles overlap) memory */
|
||||
extern void
|
||||
mem_prim_move32(uint32_t *dest, const uint32_t *src, uint32_t length);
|
||||
|
||||
|
||||
/* set bytes */
|
||||
extern void
|
||||
mem_prim_set(void *dest, uint32_t dmax, uint8_t value);
|
||||
|
||||
/* set uint16_ts */
|
||||
extern void
|
||||
mem_prim_set16(uint16_t *dest, uint32_t dmax, uint16_t value);
|
||||
|
||||
/* set uint32_ts */
|
||||
extern void
|
||||
mem_prim_set32(uint32_t *dest, uint32_t dmax, uint32_t value);
|
||||
|
||||
|
||||
#endif /* __MEM_PRIMITIVES_LIB_H__ */
|
157
casadm/safeclib/memcpy_s.c
Normal file
157
casadm/safeclib/memcpy_s.c
Normal file
@ -0,0 +1,157 @@
|
||||
/*------------------------------------------------------------------
|
||||
* memcpy_s
|
||||
*
|
||||
* October 2008, Bo Berry
|
||||
*
|
||||
* Copyright (c) 2008-2011 Cisco Systems
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "safeclib_private.h"
|
||||
#include "safe_mem_constraint.h"
|
||||
#include "mem_primitives_lib.h"
|
||||
#include "safe_mem_lib.h"
|
||||
|
||||
|
||||
/**
|
||||
* NAME
|
||||
* memcpy_s
|
||||
*
|
||||
* SYNOPSIS
|
||||
* #include "safe_mem_lib.h"
|
||||
* errno_t
|
||||
* memcpy_s(void *dest, rsize_t dmax, const void *src, rsize_t smax)
|
||||
*
|
||||
* DESCRIPTION
|
||||
* This function copies at most smax bytes from src to dest, up to
|
||||
* dmax.
|
||||
*
|
||||
* SPECIFIED IN
|
||||
* ISO/IEC JTC1 SC22 WG14 N1172, Programming languages, environments
|
||||
* and system software interfaces, Extensions to the C Library,
|
||||
* Part I: Bounds-checking interfaces
|
||||
*
|
||||
* INPUT PARAMETERS
|
||||
* dest pointer to memory that will be replaced by src.
|
||||
*
|
||||
* dmax maximum length of the resulting dest
|
||||
*
|
||||
* src pointer to the memory that will be copied to dest
|
||||
*
|
||||
* smax maximum number bytes of src to copy
|
||||
*
|
||||
* OUTPUT PARAMETERS
|
||||
* dest is updated
|
||||
*
|
||||
* RUNTIME CONSTRAINTS
|
||||
* Neither dest nor src shall be a null pointer.
|
||||
* Neither dmax nor smax shall be zero.
|
||||
* dmax shall not be greater than RSIZE_MAX_MEM.
|
||||
* smax shall not be greater than dmax.
|
||||
* Copying shall not take place between regions that overlap.
|
||||
* If there is a runtime-constraint violation, the memcpy_s function
|
||||
* stores zeros in the first dmax bytes of the region pointed to
|
||||
* by dest if dest is not a null pointer and smax is valid.
|
||||
*
|
||||
* RETURN VALUE
|
||||
* EOK successful operation
|
||||
* ESNULLP NULL pointer
|
||||
* ESZEROL zero length
|
||||
* ESLEMAX length exceeds max limit
|
||||
* ESOVRLP source memory overlaps destination
|
||||
*
|
||||
* ALSO SEE
|
||||
* memcpy16_s(), memcpy32_s(), memmove_s(), memmove16_s(),
|
||||
* memmove32_s()
|
||||
*
|
||||
*/
|
||||
errno_t
|
||||
memcpy_s (void *dest, rsize_t dmax, const void *src, rsize_t smax)
|
||||
{
|
||||
uint8_t *dp;
|
||||
const uint8_t *sp;
|
||||
|
||||
dp = dest;
|
||||
sp = src;
|
||||
|
||||
if (dp == NULL) {
|
||||
invoke_safe_mem_constraint_handler("memcpy_s: dest is NULL",
|
||||
NULL, ESNULLP);
|
||||
return RCNEGATE(ESNULLP);
|
||||
}
|
||||
|
||||
if (dmax == 0) {
|
||||
invoke_safe_mem_constraint_handler("memcpy_s: dmax is 0",
|
||||
NULL, ESZEROL);
|
||||
return RCNEGATE(ESZEROL);
|
||||
}
|
||||
|
||||
if (dmax > RSIZE_MAX_MEM) {
|
||||
invoke_safe_mem_constraint_handler("memcpy_s: dmax exceeds max",
|
||||
NULL, ESLEMAX);
|
||||
return RCNEGATE(ESLEMAX);
|
||||
}
|
||||
|
||||
if (smax == 0) {
|
||||
mem_prim_set(dp, dmax, 0);
|
||||
invoke_safe_mem_constraint_handler("memcpy_s: smax is 0",
|
||||
NULL, ESZEROL);
|
||||
return RCNEGATE(ESZEROL);
|
||||
}
|
||||
|
||||
if (smax > dmax) {
|
||||
mem_prim_set(dp, dmax, 0);
|
||||
invoke_safe_mem_constraint_handler("memcpy_s: smax exceeds dmax",
|
||||
NULL, ESLEMAX);
|
||||
return RCNEGATE(ESLEMAX);
|
||||
}
|
||||
|
||||
if (sp == NULL) {
|
||||
mem_prim_set(dp, dmax, 0);
|
||||
invoke_safe_mem_constraint_handler("memcpy_s: src is NULL",
|
||||
NULL, ESNULLP);
|
||||
return RCNEGATE(ESNULLP);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* overlap is undefined behavior, do not allow
|
||||
*/
|
||||
if( ((dp > sp) && (dp < (sp+smax))) ||
|
||||
((sp > dp) && (sp < (dp+dmax))) ) {
|
||||
mem_prim_set(dp, dmax, 0);
|
||||
invoke_safe_mem_constraint_handler("memcpy_s: overlap undefined",
|
||||
NULL, ESOVRLP);
|
||||
return RCNEGATE(ESOVRLP);
|
||||
}
|
||||
|
||||
/*
|
||||
* now perform the copy
|
||||
*/
|
||||
mem_prim_move(dp, sp, smax);
|
||||
|
||||
return RCNEGATE(EOK);
|
||||
}
|
||||
EXPORT_SYMBOL(memcpy_s);
|
148
casadm/safeclib/memmove_s.c
Normal file
148
casadm/safeclib/memmove_s.c
Normal file
@ -0,0 +1,148 @@
|
||||
/*------------------------------------------------------------------
|
||||
* memmove_s.c
|
||||
*
|
||||
* October 2008, Bo Berry
|
||||
*
|
||||
* Copyright (c) 2008-2011 Cisco Systems
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "safeclib_private.h"
|
||||
#include "safe_mem_constraint.h"
|
||||
#include "mem_primitives_lib.h"
|
||||
#include "safe_mem_lib.h"
|
||||
|
||||
/**
|
||||
* NAME
|
||||
* memmove_s
|
||||
*
|
||||
* SYNOPSIS
|
||||
* #include "safe_mem_lib.h"
|
||||
* errno_t
|
||||
* memmove_s(void *dest, rsize_t dmax,
|
||||
* const void *src, rsize_t smax)
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The memmove_s function copies smax bytes from the region pointed
|
||||
* to by src into the region pointed to by dest. This copying takes place
|
||||
* as if the smax bytes from the region pointed to by src are first copied
|
||||
* into a temporary array of smax bytes that does not overlap the region
|
||||
* pointed to by dest or src, and then the smax bytes from the temporary
|
||||
* array are copied into the object region to by dest.
|
||||
*
|
||||
* SPECIFIED IN
|
||||
* ISO/IEC TR 24731, Programming languages, environments
|
||||
* and system software interfaces, Extensions to the C Library,
|
||||
* Part I: Bounds-checking interfaces
|
||||
*
|
||||
* INPUT PARAMETERS
|
||||
* dest pointer to the memory that will be replaced by src.
|
||||
*
|
||||
* dmax maximum length of the resulting dest, in bytes
|
||||
*
|
||||
* src pointer to the memory that will be copied
|
||||
* to dest
|
||||
*
|
||||
* smax maximum number bytes of src that can be copied
|
||||
*
|
||||
* OUTPUT PARAMETERS
|
||||
* dest is updated
|
||||
*
|
||||
* RUNTIME CONSTRAINTS
|
||||
* Neither dest nor src shall be a null pointer.
|
||||
* Neither dmax nor smax shall be 0.
|
||||
* dmax shall not be greater than RSIZE_MAX_MEM.
|
||||
* smax shall not be greater than dmax.
|
||||
* If there is a runtime-constraint violation, the memmove_s function
|
||||
* stores zeros in the first dmax characters of the regionpointed to
|
||||
* by dest if dest is not a null pointer and dmax is not greater
|
||||
* than RSIZE_MAX_MEM.
|
||||
*
|
||||
* RETURN VALUE
|
||||
* EOK successful operation
|
||||
* ESNULLP NULL pointer
|
||||
* ESZEROL zero length
|
||||
* ESLEMAX length exceeds max limit
|
||||
*
|
||||
* ALSO SEE
|
||||
* memmove16_s(), memmove32_s(), memcpy_s(), memcpy16_s() memcpy32_s()
|
||||
*
|
||||
*/
|
||||
errno_t
|
||||
memmove_s (void *dest, rsize_t dmax, const void *src, rsize_t smax)
|
||||
{
|
||||
uint8_t *dp;
|
||||
const uint8_t *sp;
|
||||
|
||||
dp= dest;
|
||||
sp = src;
|
||||
|
||||
if (dp == NULL) {
|
||||
invoke_safe_mem_constraint_handler("memmove_s: dest is null",
|
||||
NULL, ESNULLP);
|
||||
return (RCNEGATE(ESNULLP));
|
||||
}
|
||||
|
||||
if (dmax == 0) {
|
||||
invoke_safe_mem_constraint_handler("memmove_s: dmax is 0",
|
||||
NULL, ESZEROL);
|
||||
return (RCNEGATE(ESZEROL));
|
||||
}
|
||||
|
||||
if (dmax > RSIZE_MAX_MEM) {
|
||||
invoke_safe_mem_constraint_handler("memmove_s: dmax exceeds max",
|
||||
NULL, ESLEMAX);
|
||||
return (RCNEGATE(ESLEMAX));
|
||||
}
|
||||
|
||||
if (smax == 0) {
|
||||
mem_prim_set(dp, dmax, 0);
|
||||
invoke_safe_mem_constraint_handler("memmove_s: smax is 0",
|
||||
NULL, ESZEROL);
|
||||
return (RCNEGATE(ESZEROL));
|
||||
}
|
||||
|
||||
if (smax > dmax) {
|
||||
mem_prim_set(dp, dmax, 0);
|
||||
invoke_safe_mem_constraint_handler("memmove_s: smax exceeds max",
|
||||
NULL, ESLEMAX);
|
||||
return (RCNEGATE(ESLEMAX));
|
||||
}
|
||||
|
||||
if (sp == NULL) {
|
||||
mem_prim_set(dp, dmax, 0);
|
||||
invoke_safe_mem_constraint_handler("memmove_s: src is null",
|
||||
NULL, ESNULLP);
|
||||
return (RCNEGATE(ESNULLP));
|
||||
}
|
||||
|
||||
/*
|
||||
* now perform the copy
|
||||
*/
|
||||
mem_prim_move(dp, sp, smax);
|
||||
|
||||
return (RCNEGATE(EOK));
|
||||
}
|
||||
EXPORT_SYMBOL(memmove_s);
|
105
casadm/safeclib/memset_s.c
Normal file
105
casadm/safeclib/memset_s.c
Normal file
@ -0,0 +1,105 @@
|
||||
/*------------------------------------------------------------------
|
||||
* memset_s
|
||||
*
|
||||
* October 2008, Bo Berry
|
||||
*
|
||||
* Copyright (c) 2008-2011 Cisco Systems
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "safeclib_private.h"
|
||||
#include "safe_mem_constraint.h"
|
||||
#include "mem_primitives_lib.h"
|
||||
#include "safe_mem_lib.h"
|
||||
|
||||
|
||||
/**
|
||||
* NAME
|
||||
* memset_s
|
||||
*
|
||||
* SYNOPSIS
|
||||
* #include "safe_mem_lib.h"
|
||||
* errno_t
|
||||
* memset_s(void *dest, rsize_t len, uint8_t value)
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Sets len bytes starting at dest to the specified value.
|
||||
*
|
||||
* SPECIFIED IN
|
||||
* ISO/IEC JTC1 SC22 WG14 N1172, Programming languages, environments
|
||||
* and system software interfaces, Extensions to the C Library,
|
||||
* Part I: Bounds-checking interfaces
|
||||
*
|
||||
* INPUT PARAMETERS
|
||||
* dest pointer to memory that will be set to the value
|
||||
*
|
||||
* len number of bytes to be set
|
||||
*
|
||||
* value byte value
|
||||
*
|
||||
* OUTPUT PARAMETERS
|
||||
* dest is updated
|
||||
*
|
||||
* RUNTIME CONSTRAINTS
|
||||
* dest shall not be a null pointer.
|
||||
* len shall not be 0 nor greater than RSIZE_MAX_MEM.
|
||||
* If there is a runtime constraint, the operation is not performed.
|
||||
*
|
||||
* RETURN VALUE
|
||||
* EOK successful operation
|
||||
* ESNULLP NULL pointer
|
||||
* ESZEROL zero length
|
||||
* ESLEMAX length exceeds max limit
|
||||
*
|
||||
* ALSO SEE
|
||||
* memset16_s(), memset32_s()
|
||||
*
|
||||
*/
|
||||
errno_t
|
||||
memset_s (void *dest, rsize_t len, uint8_t value)
|
||||
{
|
||||
if (dest == NULL) {
|
||||
invoke_safe_mem_constraint_handler("memset_s: dest is null",
|
||||
NULL, ESNULLP);
|
||||
return (RCNEGATE(ESNULLP));
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
invoke_safe_mem_constraint_handler("memset_s: len is 0",
|
||||
NULL, ESZEROL);
|
||||
return (RCNEGATE(ESZEROL));
|
||||
}
|
||||
|
||||
if (len > RSIZE_MAX_MEM) {
|
||||
invoke_safe_mem_constraint_handler("memset_s: len exceeds max",
|
||||
NULL, ESLEMAX);
|
||||
return (RCNEGATE(ESLEMAX));
|
||||
}
|
||||
|
||||
mem_prim_set(dest, len, value);
|
||||
|
||||
return (RCNEGATE(EOK));
|
||||
}
|
||||
EXPORT_SYMBOL(memset_s);
|
61
casadm/safeclib/safe_lib.h
Normal file
61
casadm/safeclib/safe_lib.h
Normal file
@ -0,0 +1,61 @@
|
||||
/*------------------------------------------------------------------
|
||||
* safe_lib.h -- Safe C Library
|
||||
*
|
||||
* October 2008, Bo Berry
|
||||
* Modified 2012, Jonathan Toppins <jtoppins@users.sourceforge.net>
|
||||
*
|
||||
* Copyright (c) 2008-2013 by Cisco Systems, Inc
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef __SAFE_LIB_H__
|
||||
#define __SAFE_LIB_H__
|
||||
|
||||
#include "safe_types.h"
|
||||
#include "safe_lib_errno.h"
|
||||
|
||||
/* C11 appendix K types - specific for bounds checking */
|
||||
typedef size_t rsize_t;
|
||||
|
||||
/*
|
||||
* We depart from the standard and allow memory and string operations to
|
||||
* have different max sizes. See the respective safe_mem_lib.h or
|
||||
* safe_str_lib.h files.
|
||||
*/
|
||||
/* #define RSIZE_MAX (~(rsize_t)0) - leave here for completeness */
|
||||
|
||||
typedef void (*constraint_handler_t) (const char * /* msg */,
|
||||
void * /* ptr */,
|
||||
errno_t /* error */);
|
||||
|
||||
extern void abort_handler_s(const char *msg, void *ptr, errno_t error);
|
||||
extern void ignore_handler_s(const char *msg, void *ptr, errno_t error);
|
||||
|
||||
#define sl_default_handler ignore_handler_s
|
||||
|
||||
#include "safe_mem_lib.h"
|
||||
#include "safe_str_lib.h"
|
||||
|
||||
#endif /* __SAFE_LIB_H__ */
|
91
casadm/safeclib/safe_lib_errno.h
Normal file
91
casadm/safeclib/safe_lib_errno.h
Normal file
@ -0,0 +1,91 @@
|
||||
/*------------------------------------------------------------------
|
||||
* safe_lib_errno.h -- Safe C Lib Error codes
|
||||
*
|
||||
* October 2008, Bo Berry
|
||||
* Modified 2012, Jonathan Toppins <jtoppins@users.sourceforge.net>
|
||||
*
|
||||
* Copyright (c) 2008-2013 by Cisco Systems, Inc
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef __SAFE_LIB_ERRNO_H__
|
||||
#define __SAFE_LIB_ERRNO_H__
|
||||
|
||||
#ifdef __KERNEL__
|
||||
# include <linux/errno.h>
|
||||
#else
|
||||
#include <errno.h>
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
/*
|
||||
* Safe Lib specific errno codes. These can be added to the errno.h file
|
||||
* if desired.
|
||||
*/
|
||||
#ifndef ESNULLP
|
||||
#define ESNULLP ( 400 ) /* null ptr */
|
||||
#endif
|
||||
|
||||
#ifndef ESZEROL
|
||||
#define ESZEROL ( 401 ) /* length is zero */
|
||||
#endif
|
||||
|
||||
#ifndef ESLEMIN
|
||||
#define ESLEMIN ( 402 ) /* length is below min */
|
||||
#endif
|
||||
|
||||
#ifndef ESLEMAX
|
||||
#define ESLEMAX ( 403 ) /* length exceeds max */
|
||||
#endif
|
||||
|
||||
#ifndef ESOVRLP
|
||||
#define ESOVRLP ( 404 ) /* overlap undefined */
|
||||
#endif
|
||||
|
||||
#ifndef ESEMPTY
|
||||
#define ESEMPTY ( 405 ) /* empty string */
|
||||
#endif
|
||||
|
||||
#ifndef ESNOSPC
|
||||
#define ESNOSPC ( 406 ) /* not enough space for s2 */
|
||||
#endif
|
||||
|
||||
#ifndef ESUNTERM
|
||||
#define ESUNTERM ( 407 ) /* unterminated string */
|
||||
#endif
|
||||
|
||||
#ifndef ESNODIFF
|
||||
#define ESNODIFF ( 408 ) /* no difference */
|
||||
#endif
|
||||
|
||||
#ifndef ESNOTFND
|
||||
#define ESNOTFND ( 409 ) /* not found */
|
||||
#endif
|
||||
|
||||
/* EOK may or may not be defined in errno.h */
|
||||
#ifndef EOK
|
||||
#define EOK ( 0 )
|
||||
#endif
|
||||
|
||||
#endif /* __SAFE_LIB_ERRNO_H__ */
|
142
casadm/safeclib/safe_mem_constraint.c
Normal file
142
casadm/safeclib/safe_mem_constraint.c
Normal file
@ -0,0 +1,142 @@
|
||||
/*------------------------------------------------------------------
|
||||
* safe_mem_constraint.c
|
||||
*
|
||||
* October 2008, Bo Berry
|
||||
* 2012, Jonathan Toppins <jtoppins@users.sourceforge.net>
|
||||
*
|
||||
* Copyright (c) 2008-2012 Cisco Systems
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "safeclib_private.h"
|
||||
#include "safe_mem_constraint.h"
|
||||
#include "safe_mem_lib.h"
|
||||
|
||||
|
||||
static constraint_handler_t mem_handler = NULL;
|
||||
|
||||
|
||||
/**
|
||||
* NAME
|
||||
* set_mem_constraint_handler_s
|
||||
*
|
||||
* SYNOPSIS
|
||||
* #include "safe_mem_lib.h"
|
||||
* constraint_handler_t
|
||||
* set_mem_constraint_handler_straint_handler_t handler)
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The set_mem_constraint_handler_s function sets the runtime-constraint
|
||||
* handler to be handler. The runtime-constraint handler is the function to
|
||||
* be called when a library function detects a runtime-constraint
|
||||
* order:
|
||||
* 1. A pointer to a character string describing the
|
||||
* runtime-constraint violation.
|
||||
* 2. A null pointer or a pointer to an implementation defined
|
||||
* object.
|
||||
* 3. If the function calling the handler has a return type declared
|
||||
* as errno_t, the return value of the function is passed.
|
||||
* Otherwise, a positive value of type errno_t is passed.
|
||||
* The implementation has a default constraint handler that is used if no
|
||||
* calls to the set_constraint_handler_s function have been made. The
|
||||
* behavior of the default handler is implementation-defined, and it may
|
||||
* cause the program to exit or abort. If the handler argument to
|
||||
* set_constraint_handler_s is a null pointer, the implementation default
|
||||
* handler becomes the current constraint handler.
|
||||
*
|
||||
* SPECIFIED IN
|
||||
* ISO/IEC JTC1 SC22 WG14 N1172, Programming languages, environments
|
||||
* and system software interfaces, Extensions to the C Library,
|
||||
* Part I: Bounds-checking interfaces
|
||||
*
|
||||
* INPUT PARAMETERS
|
||||
* *msg Pointer to the message describing the error
|
||||
*
|
||||
* *ptr Pointer to aassociated data. Can be NULL.
|
||||
*
|
||||
* error The error code encountered.
|
||||
*
|
||||
* OUTPUT PARAMETERS
|
||||
* none
|
||||
*
|
||||
* RETURN VALUE
|
||||
* none
|
||||
*
|
||||
* ALSO SEE
|
||||
* set_str_constraint_handler_s()
|
||||
*/
|
||||
constraint_handler_t
|
||||
set_mem_constraint_handler_s (constraint_handler_t handler)
|
||||
{
|
||||
constraint_handler_t prev_handler = mem_handler;
|
||||
if (NULL == handler) {
|
||||
mem_handler = sl_default_handler;
|
||||
} else {
|
||||
mem_handler = handler;
|
||||
}
|
||||
return prev_handler;
|
||||
}
|
||||
EXPORT_SYMBOL(set_mem_constraint_handler_s);
|
||||
|
||||
|
||||
/**
|
||||
* NAME
|
||||
* invoke_safe_mem_constraint_handler
|
||||
*
|
||||
* SYNOPSIS
|
||||
* #include "safe_mem_constraint.h"
|
||||
* void
|
||||
* invoke_safe_mem_constraint_handler(const char *msg,
|
||||
* void *ptr,
|
||||
* errno_t error)
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Invokes the currently set constraint handler or the default.
|
||||
*
|
||||
* INPUT PARAMETERS
|
||||
* *msg Pointer to the message describing the error
|
||||
*
|
||||
* *ptr Pointer to aassociated data. Can be NULL.
|
||||
*
|
||||
* error The error code encountered.
|
||||
*
|
||||
* OUTPUT PARAMETERS
|
||||
* none
|
||||
*
|
||||
* RETURN VALUE
|
||||
* none
|
||||
*
|
||||
*/
|
||||
void
|
||||
invoke_safe_mem_constraint_handler (const char *msg,
|
||||
void *ptr,
|
||||
errno_t error)
|
||||
{
|
||||
if (NULL != mem_handler) {
|
||||
mem_handler(msg, ptr, error);
|
||||
} else {
|
||||
sl_default_handler(msg, ptr, error);
|
||||
}
|
||||
}
|
46
casadm/safeclib/safe_mem_constraint.h
Normal file
46
casadm/safeclib/safe_mem_constraint.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*------------------------------------------------------------------
|
||||
* safe_mem_constraint.h
|
||||
*
|
||||
* October 2008, Bo Berry
|
||||
*
|
||||
* Copyright (c) 2008, 2009 by Cisco Systems, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef __SAFE_MEM_CONSTRAINT_H__
|
||||
#define __SAFE_MEM_CONSTRAINT_H__
|
||||
|
||||
#include "safeclib_private.h"
|
||||
|
||||
/*
|
||||
* Function used by the libraries to invoke the registered
|
||||
* runtime-constraint handler. Always needed.
|
||||
*/
|
||||
extern void invoke_safe_mem_constraint_handler(
|
||||
const char *msg,
|
||||
void *ptr,
|
||||
errno_t error);
|
||||
|
||||
#endif /* __SAFE_MEM_CONSTRAINT_H__ */
|
57
casadm/safeclib/safe_mem_lib.h
Normal file
57
casadm/safeclib/safe_mem_lib.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*------------------------------------------------------------------
|
||||
* safe_mem_lib.h -- Safe C Library Memory APIs
|
||||
*
|
||||
* October 2008, Bo Berry
|
||||
* Modified 2012, Jonathan Toppins <jtoppins@users.sourceforge.net>
|
||||
*
|
||||
* Copyright (c) 2008-2012 by Cisco Systems, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef __SAFE_MEM_LIB_H__
|
||||
#define __SAFE_MEM_LIB_H__
|
||||
|
||||
#include "safe_lib.h"
|
||||
|
||||
#define RSIZE_MAX_MEM ( 256UL << 20 ) /* 256MB */
|
||||
#define RSIZE_MAX_MEM16 ( RSIZE_MAX_MEM/2 )
|
||||
#define RSIZE_MAX_MEM32 ( RSIZE_MAX_MEM/4 )
|
||||
|
||||
/* set memory constraint handler */
|
||||
extern constraint_handler_t
|
||||
set_mem_constraint_handler_s(constraint_handler_t handler);
|
||||
|
||||
/* copy memory */
|
||||
extern errno_t memcpy_s(void *dest, rsize_t dmax,
|
||||
const void *src, rsize_t slen);
|
||||
|
||||
/* set bytes */
|
||||
extern errno_t memset_s(void *dest, rsize_t dmax, uint8_t value);
|
||||
|
||||
/* move memory, including overlapping memory */
|
||||
extern errno_t memmove_s(void *dest, rsize_t dmax,
|
||||
const void *src, rsize_t slen);
|
||||
|
||||
#endif /* __SAFE_MEM_LIB_H__ */
|
146
casadm/safeclib/safe_str_constraint.c
Normal file
146
casadm/safeclib/safe_str_constraint.c
Normal file
@ -0,0 +1,146 @@
|
||||
/*------------------------------------------------------------------
|
||||
* safe_str_constraint.c
|
||||
*
|
||||
* October 2008, Bo Berry
|
||||
* 2012, Jonathan Toppins <jtoppins@users.sourceforge.net>
|
||||
*
|
||||
* Copyright (c) 2008, 2009, 2012 Cisco Systems
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "safeclib_private.h"
|
||||
#include "safe_str_constraint.h"
|
||||
#include "safe_str_lib.h"
|
||||
|
||||
|
||||
static constraint_handler_t str_handler = NULL;
|
||||
|
||||
|
||||
/**
|
||||
* NAME
|
||||
* set_str_constraint_handler_s
|
||||
*
|
||||
* SYNOPSIS
|
||||
* #include "safe_str_lib.h"
|
||||
* constraint_handler_t
|
||||
* set_str_constraint_handler_s(constraint_handler_t handler)
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The set_str_constraint_handler_s function sets the runtime-constraint
|
||||
* handler to be handler. The runtime-constraint handler is the function to
|
||||
* be called when a library function detects a runtime-constraint
|
||||
* violation. Only the most recent handler registered with
|
||||
* set_str_constraint_handler_s is called when a runtime-constraint
|
||||
* violation occurs.
|
||||
* When the handler is called, it is passed the following arguments in
|
||||
* the following order:
|
||||
* 1. A pointer to a character string describing the
|
||||
* runtime-constraint violation.
|
||||
* 2. A null pointer or a pointer to an implementation defined
|
||||
* object.
|
||||
* 3. If the function calling the handler has a return type declared
|
||||
* as errno_t, the return value of the function is passed.
|
||||
* Otherwise, a positive value of type errno_t is passed.
|
||||
* The implementation has a default constraint handler that is used if no
|
||||
* calls to the set_constraint_handler_s function have been made. The
|
||||
* behavior of the default handler is implementation-defined, and it may
|
||||
* cause the program to exit or abort. If the handler argument to
|
||||
* set_constraint_handler_s is a null pointer, the implementation default
|
||||
* handler becomes the current constraint handler.
|
||||
*
|
||||
* SPECIFIED IN
|
||||
* ISO/IEC JTC1 SC22 WG14 N1172, Programming languages, environments
|
||||
* and system software interfaces, Extensions to the C Library,
|
||||
* Part I: Bounds-checking interfaces
|
||||
*
|
||||
* INPUT PARAMETERS
|
||||
* *msg Pointer to the message describing the error
|
||||
*
|
||||
* *ptr Pointer to aassociated data. Can be NULL.
|
||||
*
|
||||
* error The error code encountered.
|
||||
*
|
||||
* OUTPUT PARAMETERS
|
||||
* none
|
||||
*
|
||||
* RETURN VALUE
|
||||
* none
|
||||
*
|
||||
* ALSO SEE
|
||||
* set_str_constraint_handler_s()
|
||||
*/
|
||||
constraint_handler_t
|
||||
set_str_constraint_handler_s (constraint_handler_t handler)
|
||||
{
|
||||
constraint_handler_t prev_handler = str_handler;
|
||||
if (NULL == handler) {
|
||||
str_handler = sl_default_handler;
|
||||
} else {
|
||||
str_handler = handler;
|
||||
}
|
||||
return prev_handler;
|
||||
}
|
||||
EXPORT_SYMBOL(set_str_constraint_handler_s);
|
||||
|
||||
|
||||
/**
|
||||
* NAME
|
||||
* invoke_safe_str_constraint_handler
|
||||
*
|
||||
* SYNOPSIS
|
||||
* #include "safe_str_constraint.h"
|
||||
* void
|
||||
* invoke_safe_str_constraint_handler (const char *msg,
|
||||
* void *ptr,
|
||||
* errno_t error)
|
||||
*
|
||||
* DESCRIPTION
|
||||
* Invokes the currently set constraint handler or the default.
|
||||
*
|
||||
* INPUT PARAMETERS
|
||||
* *msg Pointer to the message describing the error
|
||||
*
|
||||
* *ptr Pointer to aassociated data. Can be NULL.
|
||||
*
|
||||
* error The error code encountered.
|
||||
*
|
||||
* OUTPUT PARAMETERS
|
||||
* none
|
||||
*
|
||||
* RETURN VALUE
|
||||
* none
|
||||
*
|
||||
*/
|
||||
void
|
||||
invoke_safe_str_constraint_handler (const char *msg,
|
||||
void *ptr,
|
||||
errno_t error)
|
||||
{
|
||||
if (NULL != str_handler) {
|
||||
str_handler(msg, ptr, error);
|
||||
} else {
|
||||
sl_default_handler(msg, ptr, error);
|
||||
}
|
||||
}
|
64
casadm/safeclib/safe_str_constraint.h
Normal file
64
casadm/safeclib/safe_str_constraint.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*------------------------------------------------------------------
|
||||
* safe_str_constraint.h
|
||||
*
|
||||
* October 2008, Bo Berry
|
||||
*
|
||||
* Copyright (c) 2008-2011 Cisco Systems
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef __SAFE_STR_CONSTRAINT_H__
|
||||
#define __SAFE_STR_CONSTRAINT_H__
|
||||
|
||||
#include "safeclib_private.h"
|
||||
|
||||
/*
|
||||
* Function used by the libraries to invoke the registered
|
||||
* runtime-constraint handler. Always needed.
|
||||
*/
|
||||
extern void invoke_safe_str_constraint_handler(
|
||||
const char *msg,
|
||||
void *ptr,
|
||||
errno_t error);
|
||||
|
||||
|
||||
/*
|
||||
* Safe C Lib internal string routine to consolidate error handling
|
||||
*/
|
||||
static inline void handle_error(char *orig_dest, rsize_t orig_dmax,
|
||||
char *err_msg, errno_t err_code)
|
||||
{
|
||||
#ifdef SAFECLIB_STR_NULL_SLACK
|
||||
/* null string to eliminate partial copy */
|
||||
while (orig_dmax) { *orig_dest = '\0'; orig_dmax--; orig_dest++; }
|
||||
#else
|
||||
*orig_dest = '\0';
|
||||
#endif
|
||||
|
||||
invoke_safe_str_constraint_handler(err_msg, NULL, err_code);
|
||||
return;
|
||||
}
|
||||
|
||||
#endif /* __SAFE_STR_CONSTRAINT_H__ */
|
71
casadm/safeclib/safe_str_lib.h
Normal file
71
casadm/safeclib/safe_str_lib.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*------------------------------------------------------------------
|
||||
* safe_str_lib.h -- Safe C Library String APIs
|
||||
*
|
||||
* October 2008, Bo Berry
|
||||
*
|
||||
* Copyright (c) 2008-2011, 2013 by Cisco Systems, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef __SAFE_STR_LIB_H__
|
||||
#define __SAFE_STR_LIB_H__
|
||||
|
||||
#include "safe_lib.h"
|
||||
|
||||
/*
|
||||
* The shortest string is a null string!!
|
||||
*/
|
||||
#define RSIZE_MIN_STR ( 1 )
|
||||
|
||||
/* maximum sring length */
|
||||
#define RSIZE_MAX_STR ( 4UL << 10 ) /* 4KB */
|
||||
|
||||
|
||||
/* The makeup of a password */
|
||||
#define SAFE_STR_MIN_LOWERCASE ( 2 )
|
||||
#define SAFE_STR_MIN_UPPERCASE ( 2 )
|
||||
#define SAFE_STR_MIN_NUMBERS ( 1 )
|
||||
#define SAFE_STR_MIN_SPECIALS ( 1 )
|
||||
|
||||
#define SAFE_STR_PASSWORD_MIN_LENGTH ( 6 )
|
||||
#define SAFE_STR_PASSWORD_MAX_LENGTH ( 32 )
|
||||
|
||||
/* set string constraint handler */
|
||||
extern constraint_handler_t
|
||||
set_str_constraint_handler_s(constraint_handler_t handler);
|
||||
|
||||
/* fitted string copy */
|
||||
extern errno_t
|
||||
strncpy_s(char *dest, rsize_t dmax, const char *src, rsize_t slen);
|
||||
|
||||
/* string length */
|
||||
extern rsize_t
|
||||
strnlen_s (const char *s, rsize_t smax);
|
||||
|
||||
/* string tokenizer */
|
||||
extern char *
|
||||
strtok_s(char *s1, rsize_t *s1max, const char *src, char **ptr);
|
||||
|
||||
#endif /* __SAFE_STR_LIB_H__ */
|
59
casadm/safeclib/safe_types.h
Normal file
59
casadm/safeclib/safe_types.h
Normal file
@ -0,0 +1,59 @@
|
||||
/*------------------------------------------------------------------
|
||||
* safe_types.h - C99 std types & defs or Linux kernel equivalents
|
||||
*
|
||||
* March 2007, Bo Berry
|
||||
* Modified 2012, Jonathan Toppins <jtoppins@users.sourceforge.net>
|
||||
*
|
||||
* Copyright (c) 2007-2013 by Cisco Systems, Inc
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef __SAFE_TYPES_H__
|
||||
#define __SAFE_TYPES_H__
|
||||
|
||||
#ifdef __KERNEL__
|
||||
/* linux kernel environment */
|
||||
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
/* errno_t isn't defined in the kernel */
|
||||
typedef int errno_t;
|
||||
|
||||
#else
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
typedef int errno_t;
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* __SAFE_TYPES_H__ */
|
93
casadm/safeclib/safeclib_private.h
Normal file
93
casadm/safeclib/safeclib_private.h
Normal file
@ -0,0 +1,93 @@
|
||||
/*------------------------------------------------------------------
|
||||
* safeclib_private.h - Internal library references
|
||||
*
|
||||
* 2012, Jonathan Toppins <jtoppins@users.sourceforge.net>
|
||||
*
|
||||
* Copyright (c) 2012, 2013 by Cisco Systems, Inc
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef __SAFECLIB_PRIVATE_H__
|
||||
#define __SAFECLIB_PRIVATE_H__
|
||||
|
||||
#ifdef __KERNEL__
|
||||
/* linux kernel environment */
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ctype.h>
|
||||
|
||||
#define RCNEGATE(x) ( -(x) )
|
||||
|
||||
#define slprintf(...) printk(KERN_EMERG __VA_ARGS__)
|
||||
#define slabort()
|
||||
#ifdef DEBUG
|
||||
#define sldebug_printf(...) printk(KERN_DEBUG __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#else /* !__KERNEL__ */
|
||||
|
||||
#if HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#ifdef STDC_HEADERS
|
||||
# include <ctype.h>
|
||||
# include <stdlib.h>
|
||||
# include <stddef.h>
|
||||
#else
|
||||
# ifdef HAVE_STDLIB_H
|
||||
# include <stdlib.h>
|
||||
# endif
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
|
||||
# include <memory.h>
|
||||
# endif
|
||||
# include <string.h>
|
||||
#endif
|
||||
#ifdef HAVE_LIMITS_H
|
||||
# include <limits.h>
|
||||
#endif
|
||||
|
||||
#define EXPORT_SYMBOL(sym)
|
||||
#define RCNEGATE(x) (x)
|
||||
|
||||
#define slprintf(...) fprintf(stderr, __VA_ARGS__)
|
||||
#define slabort() abort()
|
||||
#ifdef DEBUG
|
||||
#define sldebug_printf(...) printf(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#ifndef sldebug_printf
|
||||
#define sldebug_printf(...)
|
||||
#endif
|
||||
|
||||
#include "safe_lib.h"
|
||||
|
||||
#endif /* __SAFECLIB_PRIVATE_H__ */
|
238
casadm/safeclib/strncpy_s.c
Normal file
238
casadm/safeclib/strncpy_s.c
Normal file
@ -0,0 +1,238 @@
|
||||
/*------------------------------------------------------------------
|
||||
* strncpy_s.c
|
||||
*
|
||||
* October 2008, Bo Berry
|
||||
*
|
||||
* Copyright (c) 2008-2011 by Cisco Systems, Inc
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "safeclib_private.h"
|
||||
#include "safe_str_constraint.h"
|
||||
#include "safe_str_lib.h"
|
||||
|
||||
|
||||
/*
|
||||
* NAME
|
||||
* strncpy_s
|
||||
*
|
||||
* SYNOPSIS
|
||||
* #include "safe_str_lib.h"
|
||||
* errno_t
|
||||
* strncpy_s(char *dest, rsize_t dmax, const char *src, rsize_t slen)
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The strncpy_s function copies not more than slen successive characters
|
||||
* (characters that follow a null character are not copied) from the
|
||||
* array pointed to by src to the array pointed to by dest. If no null
|
||||
* character was copied from src, then dest[n] is set to a null character.
|
||||
*
|
||||
* All elements following the terminating null character (if any)
|
||||
* written by strncpy_s in the array of dmax characters pointed to
|
||||
* by dest take on the null value when strncpy_s returns.
|
||||
*
|
||||
* Specicified in:
|
||||
* ISO/IEC TR 24731-1, Programming languages, environments
|
||||
* and system software interfaces, Extensions to the C Library,
|
||||
* Part I: Bounds-checking interfaces
|
||||
*
|
||||
* INPUT PARAMETERS
|
||||
* dest pointer to string that will be replaced by src.
|
||||
* The resulting string is null terminated.
|
||||
*
|
||||
* dmax restricted maximum length of the resulting dest,
|
||||
* including the null
|
||||
*
|
||||
* src pointer to the string that will be copied
|
||||
* to string dest
|
||||
*
|
||||
* slen the maximum number of characters to copy from src
|
||||
*
|
||||
* OUTPUT PARAMETERS
|
||||
* dest updated with src string
|
||||
*
|
||||
* RUNTIME CONSTRAINTS
|
||||
* Neither dmax nor slen shall be equal to zero.
|
||||
* Neither dmax nor slen shall be equal zero.
|
||||
* Neither dmax nor slen shall be greater than RSIZE_MAX_STR.
|
||||
* If slen is either greater than or equal to dmax, then dmax
|
||||
* should be more than strnlen_s(src,dmax)
|
||||
* Copying shall not take place between objects that overlap.
|
||||
* If there is a runtime-constraint violation, then if dest
|
||||
* is not a null pointer and dmax greater than RSIZE_MAX_STR,
|
||||
* then strncpy_s nulls dest.
|
||||
*
|
||||
* RETURN VALUE
|
||||
* EOK successful operation, the characters in src were copied
|
||||
* to dest and the result is null terminated.
|
||||
* ESNULLP NULL pointer
|
||||
* ESZEROL zero length
|
||||
* ESLEMAX length exceeds max limit
|
||||
* ESOVRLP strings overlap
|
||||
* ESNOSPC not enough space to copy src
|
||||
*
|
||||
* ALSO SEE
|
||||
* strcat_s(), strncat_s(), strcpy_s()
|
||||
*-
|
||||
*/
|
||||
errno_t
|
||||
strncpy_s (char *dest, rsize_t dmax, const char *src, rsize_t slen)
|
||||
{
|
||||
rsize_t orig_dmax;
|
||||
char *orig_dest;
|
||||
const char *overlap_bumper;
|
||||
|
||||
if (dest == NULL) {
|
||||
invoke_safe_str_constraint_handler("strncpy_s: dest is null",
|
||||
NULL, ESNULLP);
|
||||
return RCNEGATE(ESNULLP);
|
||||
}
|
||||
|
||||
if (dmax == 0) {
|
||||
invoke_safe_str_constraint_handler("strncpy_s: dmax is 0",
|
||||
NULL, ESZEROL);
|
||||
return RCNEGATE(ESZEROL);
|
||||
}
|
||||
|
||||
if (dmax > RSIZE_MAX_STR) {
|
||||
invoke_safe_str_constraint_handler("strncpy_s: dmax exceeds max",
|
||||
NULL, ESLEMAX);
|
||||
return RCNEGATE(ESLEMAX);
|
||||
}
|
||||
|
||||
/* hold base in case src was not copied */
|
||||
orig_dmax = dmax;
|
||||
orig_dest = dest;
|
||||
|
||||
if (src == NULL) {
|
||||
handle_error(orig_dest, orig_dmax, "strncpy_s: "
|
||||
"src is null",
|
||||
ESNULLP);
|
||||
return RCNEGATE(ESNULLP);
|
||||
}
|
||||
|
||||
if (slen == 0) {
|
||||
handle_error(orig_dest, orig_dmax, "strncpy_s: "
|
||||
"slen is zero",
|
||||
ESZEROL);
|
||||
return RCNEGATE(ESZEROL);
|
||||
}
|
||||
|
||||
if (slen > RSIZE_MAX_STR) {
|
||||
handle_error(orig_dest, orig_dmax, "strncpy_s: "
|
||||
"slen exceeds max",
|
||||
ESLEMAX);
|
||||
return RCNEGATE(ESLEMAX);
|
||||
}
|
||||
|
||||
|
||||
if (dest < src) {
|
||||
overlap_bumper = src;
|
||||
|
||||
while (dmax > 0) {
|
||||
if (dest == overlap_bumper) {
|
||||
handle_error(orig_dest, orig_dmax, "strncpy_s: "
|
||||
"overlapping objects",
|
||||
ESOVRLP);
|
||||
return RCNEGATE(ESOVRLP);
|
||||
}
|
||||
|
||||
if (slen == 0) {
|
||||
/*
|
||||
* Copying truncated to slen chars. Note that the TR says to
|
||||
* copy slen chars plus the null char. We null the slack.
|
||||
*/
|
||||
#ifdef SAFECLIB_STR_NULL_SLACK
|
||||
while (dmax) { *dest = '\0'; dmax--; dest++; }
|
||||
#else
|
||||
*dest = '\0';
|
||||
#endif
|
||||
return RCNEGATE(EOK);
|
||||
}
|
||||
|
||||
*dest = *src;
|
||||
if (*dest == '\0') {
|
||||
#ifdef SAFECLIB_STR_NULL_SLACK
|
||||
/* null slack */
|
||||
while (dmax) { *dest = '\0'; dmax--; dest++; }
|
||||
#endif
|
||||
return RCNEGATE(EOK);
|
||||
}
|
||||
|
||||
dmax--;
|
||||
slen--;
|
||||
dest++;
|
||||
src++;
|
||||
}
|
||||
|
||||
} else {
|
||||
overlap_bumper = dest;
|
||||
|
||||
while (dmax > 0) {
|
||||
if (src == overlap_bumper) {
|
||||
handle_error(orig_dest, orig_dmax, "strncpy_s: "
|
||||
"overlapping objects",
|
||||
ESOVRLP);
|
||||
return RCNEGATE(ESOVRLP);
|
||||
}
|
||||
|
||||
if (slen == 0) {
|
||||
/*
|
||||
* Copying truncated to slen chars. Note that the TR says to
|
||||
* copy slen chars plus the null char. We null the slack.
|
||||
*/
|
||||
#ifdef SAFECLIB_STR_NULL_SLACK
|
||||
while (dmax) { *dest = '\0'; dmax--; dest++; }
|
||||
#else
|
||||
*dest = '\0';
|
||||
#endif
|
||||
return RCNEGATE(EOK);
|
||||
}
|
||||
|
||||
*dest = *src;
|
||||
if (*dest == '\0') {
|
||||
#ifdef SAFECLIB_STR_NULL_SLACK
|
||||
/* null slack */
|
||||
while (dmax) { *dest = '\0'; dmax--; dest++; }
|
||||
#endif
|
||||
return RCNEGATE(EOK);
|
||||
}
|
||||
|
||||
dmax--;
|
||||
slen--;
|
||||
dest++;
|
||||
src++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* the entire src was not copied, so zero the string
|
||||
*/
|
||||
handle_error(orig_dest, orig_dmax, "strncpy_s: not enough "
|
||||
"space for src",
|
||||
ESNOSPC);
|
||||
return RCNEGATE(ESNOSPC);
|
||||
}
|
||||
EXPORT_SYMBOL(strncpy_s);
|
117
casadm/safeclib/strnlen_s.c
Normal file
117
casadm/safeclib/strnlen_s.c
Normal file
@ -0,0 +1,117 @@
|
||||
/*------------------------------------------------------------------
|
||||
* strnlen_s.c
|
||||
*
|
||||
* October 2008, Bo Berry
|
||||
*
|
||||
* Copyright (c) 2008-2011 by Cisco Systems, Inc
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "safeclib_private.h"
|
||||
#include "safe_str_constraint.h"
|
||||
#include "safe_str_lib.h"
|
||||
|
||||
|
||||
/**
|
||||
* NAME
|
||||
* strnlen_s
|
||||
*
|
||||
* SYNOPSIS
|
||||
* #include "safe_str_lib.h"
|
||||
* rsize_t
|
||||
* strnlen_s(const char *dest, rsize_t dmax)
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The strnlen_s function computes the length of the string pointed
|
||||
* to by dest.
|
||||
*
|
||||
* SPECIFIED IN
|
||||
* ISO/IEC TR 24731-1, Programming languages, environments
|
||||
* and system software interfaces, Extensions to the C Library,
|
||||
* Part I: Bounds-checking interfaces
|
||||
*
|
||||
* INPUT PARAMETERS
|
||||
* dest pointer to string
|
||||
*
|
||||
* dmax restricted maximum length (including null character).
|
||||
*
|
||||
* OUTPUT PARAMETERS
|
||||
* none
|
||||
*
|
||||
* RUNTIME CONSTRAINTS
|
||||
* dest shall not be a null pointer
|
||||
* dmax shall not be greater than RSIZE_MAX_STR
|
||||
* dmax shall not equal zero
|
||||
* null character shall be in first dmax characters of dest
|
||||
*
|
||||
* RETURN VALUE
|
||||
* The function returns the string length, excluding the terminating
|
||||
* null character. If dest is NULL, then strnlen_s returns 0.
|
||||
*
|
||||
* Otherwise, the strnlen_s function returns the number of characters
|
||||
* that precede the terminating null character.
|
||||
* At most the first dmax characters of dest are accessed by strnlen_s.
|
||||
*
|
||||
* ALSO SEE
|
||||
* strnterminate_s()
|
||||
*
|
||||
*/
|
||||
rsize_t
|
||||
strnlen_s (const char *dest, rsize_t dmax)
|
||||
{
|
||||
rsize_t count;
|
||||
rsize_t orig_dmax = dmax;
|
||||
|
||||
if (dest == NULL) {
|
||||
return RCNEGATE(0);
|
||||
}
|
||||
|
||||
if (dmax == 0) {
|
||||
invoke_safe_str_constraint_handler("strnlen_s: dmax is 0",
|
||||
NULL, ESZEROL);
|
||||
return RCNEGATE(0);
|
||||
}
|
||||
|
||||
if (dmax > RSIZE_MAX_STR) {
|
||||
invoke_safe_str_constraint_handler("strnlen_s: dmax exceeds max",
|
||||
NULL, ESLEMAX);
|
||||
return RCNEGATE(0);
|
||||
}
|
||||
|
||||
count = 0;
|
||||
while (*dest && dmax) {
|
||||
count++;
|
||||
dmax--;
|
||||
dest++;
|
||||
}
|
||||
if (count == orig_dmax) {
|
||||
invoke_safe_str_constraint_handler("strnlen_s: string length exceeds dmax",
|
||||
NULL, ESLEMAX);
|
||||
return RCNEGATE(0);
|
||||
}
|
||||
|
||||
return RCNEGATE(count);
|
||||
}
|
||||
EXPORT_SYMBOL(strnlen_s);
|
323
casadm/safeclib/strtok_s.c
Normal file
323
casadm/safeclib/strtok_s.c
Normal file
@ -0,0 +1,323 @@
|
||||
/*------------------------------------------------------------------
|
||||
* strtok_s.c
|
||||
*
|
||||
* October 2008, Bo Berry
|
||||
*
|
||||
* Copyright (c) 2008-2011 by Cisco Systems, Inc
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "safeclib_private.h"
|
||||
#include "safe_str_constraint.h"
|
||||
#include "safe_str_lib.h"
|
||||
|
||||
|
||||
/**
|
||||
* NAME
|
||||
* strtok_s
|
||||
*
|
||||
* SYNOPSIS
|
||||
* #include "safe_str_lib.h"
|
||||
* char *
|
||||
* strtok_s(char *dest, rsize_t *dmax, char *src, char **ptr)
|
||||
*
|
||||
* DESCRIPTION
|
||||
* A sequence of calls to the strtok_s function breaks the string
|
||||
* pointed to by dest into a sequence of tokens, each of which is
|
||||
* delimited by a character from the string pointed to by src. The
|
||||
* fourth argument points to a caller-provided char pointer into
|
||||
* which the strtok_s function stores information necessary for
|
||||
* it to continue scanning the same string.
|
||||
*
|
||||
* The first call in a sequence has a non-null first argument and
|
||||
* dmax points to an object whose value is the number of elements
|
||||
* in the character array pointed to by the first argument. The
|
||||
* first call stores an initial value in the object pointed to by
|
||||
* ptr and updates the value pointed to by dmax to reflect the
|
||||
* number of elements that remain in relation to ptr. Subsequent
|
||||
* calls in the sequence have a null first argument and the objects
|
||||
* pointed to by dmax and ptr are required to have the values
|
||||
* stored by the previous call in the sequence, which are then
|
||||
* updated. The separator string pointed to by src may be different
|
||||
* from call to call.
|
||||
*
|
||||
* The first call in the sequence searches the string pointed to
|
||||
* by dest for the first character that is not contained in the
|
||||
* current separator string pointed to by src. If no such character
|
||||
* is found, then there are no tokens in the string pointed to
|
||||
* by dest and the strtok_s function returns a null pointer. If
|
||||
* such a character is found, it is the start of the first token.
|
||||
*
|
||||
* The strtok_s function then searches from there for the first
|
||||
* character in dest that is contained in the current separator
|
||||
* string. If no such character is found, the current token
|
||||
* extends to the end of the string pointed to by dest, and
|
||||
* subsequent searches in the same string for a token return
|
||||
* a null pointer. If such a character is found, it is
|
||||
* overwritten by a null character, which terminates the
|
||||
* current token.
|
||||
*
|
||||
* In all cases, the strtok_s function stores sufficient information
|
||||
* in the pointer pointed to by ptr so that subsequent calls,
|
||||
* with a null pointer for dest and the unmodified pointer value
|
||||
* for ptr, shall start searching just past the element overwritten
|
||||
* by a null character (if any).
|
||||
*
|
||||
* SPECIFIED IN
|
||||
* ISO/IEC TR 24731-1, Programming languages, environments
|
||||
* and system software interfaces, Extensions to the C Library,
|
||||
* Part I: Bounds-checking interfaces
|
||||
*
|
||||
* INPUT PARAMETERS
|
||||
* dest pointer to string to tokenize
|
||||
*
|
||||
* dmax restricted maximum length of dest string
|
||||
*
|
||||
* src pointer to delimiter string (len < 255)
|
||||
*
|
||||
* ptr returned pointer to token
|
||||
*
|
||||
* OUTPUT PARAMETERS
|
||||
* dmax update length
|
||||
*
|
||||
* ptr update pointer to token
|
||||
*
|
||||
* RUNTIME CONSTRAINTS
|
||||
* src shall not be a null pointer.
|
||||
* ptr shall not be a null pointer.
|
||||
* dmax shall not be a null pointer.
|
||||
* *dmax shall not be 0.
|
||||
*
|
||||
* If dest is a null pointer, then *ptr shall not be a null pointer.
|
||||
*
|
||||
* dest must not be unterminated.
|
||||
*
|
||||
* The value of *dmax shall not be greater than RSIZE_MAX_STR. The
|
||||
* end of the token found shall occur within the first *dmax
|
||||
* characters of dest for the first call, and shall occur within
|
||||
* the first *dmax characters of where searching resumes on
|
||||
* subsequent calls.
|
||||
*
|
||||
* RETURN VALUE
|
||||
* The strtok_s function returns a pointer to the first character
|
||||
* of a token; or a null pointer if there is no token or there
|
||||
* is a runtime-constraint violation.
|
||||
*
|
||||
* EOK
|
||||
* ESNULLP NULL pointer
|
||||
* ESZEROL zero length
|
||||
* ESLEMAX length exceeds max limit
|
||||
* ESUNTERM unterminated string
|
||||
*
|
||||
* EXAMPLES
|
||||
* [1] Sequencial strtok_s() calls to tokenize a string
|
||||
*
|
||||
* String to tokenize str1 = ",.:*one,two;three,;four*.*.five-six***"
|
||||
* len=38
|
||||
* String of delimiters str2 = ",.;*"
|
||||
*
|
||||
* p2tok = strtok_s(str1, &len, str2, &p2str);
|
||||
* token -one- remaining -two;three,;four*.*.five-six***- len=30
|
||||
*
|
||||
* p2tok = strtok_s(NULL, &len, str2, &p2str);
|
||||
* token -two- remaining -three,;four*.*.five-six***- len=26
|
||||
*
|
||||
* p2tok = strtok_s(NULL, &len, str2, &p2str);
|
||||
* token -three- remaining -;four*.*.five-six***- len=20
|
||||
*
|
||||
* p2tok = strtok_s(NULL, &len, str2, &p2str);
|
||||
* token -four- remaining -.*.five-six***- len=14
|
||||
*
|
||||
* p2tok = strtok_s(NULL, &len, str2, &p2str);
|
||||
* token -five-six- remaining -**- len=2
|
||||
*
|
||||
* p2tok = strtok_s(NULL, &len, str2, &p2str);
|
||||
* token -(null)- remaining -**- len=0
|
||||
*
|
||||
*
|
||||
* [2] While loop with same entry data as [1]
|
||||
*
|
||||
* p2tok = str1;
|
||||
* while (p2tok && len) {
|
||||
* p2tok = strtok_s(NULL, &len, str2, &p2str);
|
||||
* printf(" token -- remaining -- len=0 \n",
|
||||
* p2tok, p2str, (int)len );
|
||||
* }
|
||||
*
|
||||
*-
|
||||
*/
|
||||
char *
|
||||
strtok_s(char *dest, rsize_t *dmax, const char *src, char **ptr)
|
||||
{
|
||||
|
||||
/*
|
||||
* CONFIGURE: The spec does not call out a maximum for the src
|
||||
* string, so one is defined here.
|
||||
*/
|
||||
#define STRTOK_DELIM_MAX_LEN ( 16 )
|
||||
|
||||
|
||||
const char *pt;
|
||||
char *ptoken;
|
||||
rsize_t dlen;
|
||||
rsize_t slen;
|
||||
|
||||
if (dmax == NULL) {
|
||||
invoke_safe_str_constraint_handler("strtok_s: dmax is NULL",
|
||||
NULL, ESNULLP);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (*dmax == 0) {
|
||||
invoke_safe_str_constraint_handler("strtok_s: dmax is 0",
|
||||
NULL, ESZEROL);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (*dmax > RSIZE_MAX_STR) {
|
||||
invoke_safe_str_constraint_handler("strtok_s: dmax exceeds max",
|
||||
NULL, ESLEMAX);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (src == NULL) {
|
||||
invoke_safe_str_constraint_handler("strtok_s: src is null",
|
||||
NULL, ESNULLP);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (ptr == NULL) {
|
||||
invoke_safe_str_constraint_handler("strtok_s: ptr is null",
|
||||
NULL, ESNULLP);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* if the source was NULL, use the tokenizer context */
|
||||
if (dest == NULL) {
|
||||
dest = *ptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* scan dest for a delimiter
|
||||
*/
|
||||
dlen = *dmax;
|
||||
ptoken = NULL;
|
||||
while (*dest != '\0' && !ptoken) {
|
||||
|
||||
if (dlen == 0) {
|
||||
*ptr = NULL;
|
||||
invoke_safe_str_constraint_handler(
|
||||
"strtok_s: dest is unterminated",
|
||||
NULL, ESUNTERM);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* must scan the entire delimiter list
|
||||
* ISO should have included a delimiter string limit!!
|
||||
*/
|
||||
slen = STRTOK_DELIM_MAX_LEN;
|
||||
pt = src;
|
||||
while (*pt != '\0') {
|
||||
|
||||
if (slen == 0) {
|
||||
*ptr = NULL;
|
||||
invoke_safe_str_constraint_handler(
|
||||
"strtok_s: src is unterminated",
|
||||
NULL, ESUNTERM);
|
||||
return (NULL);
|
||||
}
|
||||
slen--;
|
||||
|
||||
if (*dest == *pt) {
|
||||
ptoken = NULL;
|
||||
break;
|
||||
} else {
|
||||
pt++;
|
||||
ptoken = dest;
|
||||
}
|
||||
}
|
||||
dest++;
|
||||
dlen--;
|
||||
}
|
||||
|
||||
/*
|
||||
* if the beginning of a token was not found, then no
|
||||
* need to continue the scan.
|
||||
*/
|
||||
if (ptoken == NULL) {
|
||||
*dmax = dlen;
|
||||
return (ptoken);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now we need to locate the end of the token
|
||||
*/
|
||||
while (*dest != '\0') {
|
||||
|
||||
if (dlen == 0) {
|
||||
*ptr = NULL;
|
||||
invoke_safe_str_constraint_handler(
|
||||
"strtok_s: dest is unterminated",
|
||||
NULL, ESUNTERM);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
slen = STRTOK_DELIM_MAX_LEN;
|
||||
pt = src;
|
||||
while (*pt != '\0') {
|
||||
|
||||
if (slen == 0) {
|
||||
*ptr = NULL;
|
||||
invoke_safe_str_constraint_handler(
|
||||
"strtok_s: src is unterminated",
|
||||
NULL, ESUNTERM);
|
||||
return (NULL);
|
||||
}
|
||||
slen--;
|
||||
|
||||
if (*dest == *pt) {
|
||||
/*
|
||||
* found a delimiter, set to null
|
||||
* and return context ptr to next char
|
||||
*/
|
||||
*dest = '\0';
|
||||
*ptr = (dest + 1); /* return pointer for next scan */
|
||||
*dmax = dlen - 1; /* account for the nulled delimiter */
|
||||
return (ptoken);
|
||||
} else {
|
||||
/*
|
||||
* simply scanning through the delimiter string
|
||||
*/
|
||||
pt++;
|
||||
}
|
||||
}
|
||||
dest++;
|
||||
dlen--;
|
||||
}
|
||||
|
||||
*dmax = dlen;
|
||||
return (ptoken);
|
||||
}
|
1308
casadm/statistics_model.c
Normal file
1308
casadm/statistics_model.c
Normal file
File diff suppressed because it is too large
Load Diff
117
casadm/statistics_view.c
Normal file
117
casadm/statistics_view.c
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "cas_lib.h"
|
||||
#include "csvparse.h"
|
||||
#include "string.h"
|
||||
#include "statistics_view.h"
|
||||
#include "statistics_view_structs.h"
|
||||
#include "statistics_view_text.h"
|
||||
#include "statistics_view_csv.h"
|
||||
#include "statistics_view_raw_csv.h"
|
||||
|
||||
static struct view_t *construct_view(int format, FILE *outfile)
|
||||
{
|
||||
struct view_t *out = calloc(1, sizeof(*out));
|
||||
if (!out) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case CSV:
|
||||
out->process_row = csv_process_row;
|
||||
out->end_input = csv_end_input;
|
||||
out->construct = csv_construct;
|
||||
out->destruct = csv_destruct;
|
||||
break;
|
||||
case RAW_CSV:
|
||||
out->process_row = raw_csv_process_row;
|
||||
out->end_input = raw_csv_end_input;
|
||||
out->construct = raw_csv_construct;
|
||||
out->destruct = raw_csv_destruct;
|
||||
break;
|
||||
case TEXT:
|
||||
out->process_row = text_process_row;
|
||||
out->end_input = text_end_input;
|
||||
out->construct = text_construct;
|
||||
out->destruct = text_destruct;
|
||||
break;
|
||||
}
|
||||
out->outfile = outfile;
|
||||
out->construct(out);
|
||||
return out;
|
||||
};
|
||||
|
||||
void destruct_view(struct view_t* v)
|
||||
{
|
||||
v->destruct(v);
|
||||
free(v);
|
||||
}
|
||||
|
||||
#define RECOGNIZE_TYPE(t) if (!strcmp(cols[0], TAG_NAME(t))) {type = t;}
|
||||
|
||||
int stat_print_intermediate(FILE *infile, FILE *outfile)
|
||||
{
|
||||
char buf[MAX_STR_LEN] = { 0 };
|
||||
while (fgets(buf, MAX_STR_LEN, infile)) {
|
||||
fprintf(outfile, "%s", buf);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
int stat_format_output(FILE *infile, FILE *outfile, int format)
|
||||
{
|
||||
int result = 0;
|
||||
if (format == PLAIN) {
|
||||
return stat_print_intermediate(infile, outfile);
|
||||
}
|
||||
struct view_t *view = construct_view(format, outfile);
|
||||
if (!view) {
|
||||
cas_printf(LOG_ERR, "Failed to allocate memory for output generator\n");
|
||||
return 1;
|
||||
}
|
||||
CSVFILE *cf = csv_fopen(infile);
|
||||
if (!cf) {
|
||||
cas_printf(LOG_ERR, "Failed to allocate memory for CSV parser\n");
|
||||
destruct_view(view);
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (!csv_read(cf)) {
|
||||
int num_cols = csv_count_cols(cf);
|
||||
char **cols = csv_get_col_ptr(cf);
|
||||
int type = UNDEFINED_TAG;
|
||||
if (num_cols<1) {
|
||||
continue;
|
||||
}
|
||||
RECOGNIZE_TYPE(FREEFORM);
|
||||
RECOGNIZE_TYPE(KV_PAIR);
|
||||
RECOGNIZE_TYPE(TABLE_ROW);
|
||||
RECOGNIZE_TYPE(TABLE_HEADER);
|
||||
RECOGNIZE_TYPE(TABLE_SECTION);
|
||||
RECOGNIZE_TYPE(TREE_HEADER);
|
||||
RECOGNIZE_TYPE(TREE_BRANCH);
|
||||
RECOGNIZE_TYPE(TREE_LEAF);
|
||||
RECOGNIZE_TYPE(RECORD);
|
||||
RECOGNIZE_TYPE(DATA_SET);
|
||||
if (type == UNDEFINED_TAG) {
|
||||
cas_printf(LOG_ERR, "Unrecognized tag: %s\n", cols[0]);
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
if (view->process_row(view, type, num_cols-1, cols+1)) {
|
||||
cas_printf(LOG_ERR, "Failed to process row starting with: %s\n", cols[0]);
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
view->end_input(view);
|
||||
|
||||
csv_close_nu(cf);
|
||||
destruct_view(view);
|
||||
return result;
|
||||
}
|
79
casadm/statistics_view.h
Normal file
79
casadm/statistics_view.h
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef __STAT_VIEW
|
||||
#define __STAT_VIEW
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* each line of statistics may be assigned one fo these semantic formats,
|
||||
* to which it will be converted */
|
||||
|
||||
enum tag_type {
|
||||
FREEFORM, /**< free form text */
|
||||
KV_PAIR, /**< key value pair. sequence of kv-pairs will be aligned to
|
||||
columns, but no table-styleborders will be drawn */
|
||||
TABLE_ROW, /**< regular table row */
|
||||
TABLE_HEADER, /**< table header */
|
||||
TABLE_SECTION, /**< first row of a table section */
|
||||
DATA_SET, /**< set of records */
|
||||
RECORD, /**< one record of data */
|
||||
TREE_HEADER,
|
||||
TREE_BRANCH,
|
||||
TREE_LEAF,
|
||||
UNDEFINED_TAG /**< occurence of this (or anything else out of
|
||||
above tags) will immediately break processing */
|
||||
};
|
||||
|
||||
#define TAG(x) #x ","
|
||||
#define TAG_NAME(x) #x
|
||||
|
||||
enum format {
|
||||
TEXT, /**< output in text (formatted tables) form */
|
||||
CSV, /**< output in csv form */
|
||||
RAW_CSV, /**< csv form without transformations */
|
||||
PLAIN /**<debug setting: print intermediate format */
|
||||
};
|
||||
|
||||
/**
|
||||
* @param infile - file in statistics_view intermediate format
|
||||
* @param outfile - file to which statistics need to be printed
|
||||
* @param format - desired format of an output.
|
||||
*/
|
||||
int stat_format_output(FILE *infile, FILE *outfile, int format);
|
||||
|
||||
/*
|
||||
* EXAMPLE OF AN INTERMEDIATE FORMAT:
|
||||
*
|
||||
* DATA_SET,
|
||||
* RECORD,
|
||||
* KV_PAIR,Cache Id, 1
|
||||
* KV_PAIR,Cache Size, 5425999, [4KiB Blocks], 20.70, [GiB]
|
||||
* KV_PAIR,Cache Occupancy, 1340, [4KiB Blocks], 0.01, [GiB], 0.02, [%]
|
||||
* KV_PAIR,Metadata end offset, 79025
|
||||
* KV_PAIR,Dirty cache lines, 0
|
||||
* KV_PAIR,Clean cache lines, 1340
|
||||
* KV_PAIR,Cache Device, /dev/sdb
|
||||
* KV_PAIR,Core Devices, 15
|
||||
* KV_PAIR,Write Policy, wt
|
||||
* KV_PAIR,Eviction Policy, lru
|
||||
* KV_PAIR,Cleaning Policy, alru
|
||||
* KV_PAIR,Metadata Variant, "max (Maximum Performance, default)"
|
||||
* KV_PAIR,Metadata Memory Footprint, 345.4, [MiB]
|
||||
* KV_PAIR,Status, Running
|
||||
* TABLE_HEADER,Request statistics,Count,%
|
||||
* TABLE_SECTION,"Read hits", 180, 11.6
|
||||
* TABLE_ROW,"Read partial misses", 1, 0.1
|
||||
* TABLE_ROW,"Read full misses", 1370, 88.3
|
||||
* TABLE_ROW,"Read total", 1551, 100.0
|
||||
* TABLE_SECTION,"Write hits", 0, 0.0
|
||||
* TABLE_ROW,"Write partial misses", 0, 0.0
|
||||
* TABLE_ROW,"Write full misses", 0, 0.0
|
||||
* TABLE_ROW,"Write total", 0, 0.0
|
||||
*
|
||||
* In each of output formats, first CSV column (referred to as a "tag")
|
||||
* will be removed from output and used by formatter thread as a hint
|
||||
*/
|
||||
#endif /*__STAT_VIEW */
|
320
casadm/statistics_view_csv.c
Normal file
320
casadm/statistics_view_csv.c
Normal file
@ -0,0 +1,320 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "statistics_view.h"
|
||||
#include "statistics_view_structs.h"
|
||||
#include "statistics_view_csv.h"
|
||||
|
||||
#define VALS_BUFFER_INIT_SIZE 10
|
||||
|
||||
/**
|
||||
* private data of CSV output formatter
|
||||
*/
|
||||
struct csv_out_prv {
|
||||
int data_set; /* current data set number */
|
||||
int record; /* current record number */
|
||||
int column; /* current column number */
|
||||
char **vals;
|
||||
char **titles;
|
||||
int max_vals;
|
||||
int cur_val;
|
||||
int max_titles;
|
||||
int cur_title;
|
||||
};
|
||||
|
||||
static inline int csv_is_first_record(struct view_t *this)
|
||||
{
|
||||
return 1 == this->ctx.csv_prv->record;
|
||||
}
|
||||
|
||||
static inline int csv_is_unit_string(const char *s)
|
||||
{
|
||||
return NULL != s && '[' == s[0];
|
||||
}
|
||||
|
||||
static void csv_output_column(struct view_t *this, const char *s)
|
||||
{
|
||||
struct csv_out_prv *prv = this->ctx.csv_prv;
|
||||
|
||||
if (prv->column) {
|
||||
putc(',', this->outfile);
|
||||
}
|
||||
|
||||
if (strstr(s, ",")) {
|
||||
fprintf(this->outfile, "\"%s\"", s);
|
||||
} else {
|
||||
fprintf(this->outfile, "%s", s);
|
||||
}
|
||||
prv->column++;
|
||||
}
|
||||
|
||||
static char **csv_check_container(char **container, int *max_vals,
|
||||
int cur_val)
|
||||
{
|
||||
if (!container) {
|
||||
*max_vals = VALS_BUFFER_INIT_SIZE;
|
||||
container = calloc(sizeof(char *), *max_vals);
|
||||
if (!container) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Resize val pointers array if needed */
|
||||
if (*max_vals < cur_val) {
|
||||
*max_vals = *max_vals * 2;
|
||||
if (*max_vals < cur_val) {
|
||||
*max_vals = cur_val;
|
||||
}
|
||||
container = realloc(container, sizeof(char *) * (*max_vals));
|
||||
if (!container) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
static int csv_output_data(struct view_t *this, const char *s)
|
||||
{
|
||||
struct csv_out_prv *prv = this->ctx.csv_prv;
|
||||
if (csv_is_first_record(this)) {
|
||||
prv->vals = csv_check_container(prv->vals, &prv->max_vals,
|
||||
prv->cur_val+1);
|
||||
if (!prv->vals) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Store value */
|
||||
prv->vals[prv->cur_val] = strdup(s);
|
||||
if (!prv->vals[prv->cur_val]) {
|
||||
return 1;
|
||||
}
|
||||
prv->cur_val++;
|
||||
} else {
|
||||
csv_output_column(this, s);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int csv_add_column_subtitle(struct view_t *this, const char *s)
|
||||
{
|
||||
struct csv_out_prv *prv = this->ctx.csv_prv;
|
||||
|
||||
prv->titles = csv_check_container(prv->titles, &prv->max_titles,
|
||||
prv->cur_title+1);
|
||||
if (!prv->titles) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Store value */
|
||||
prv->titles[prv->cur_title] = strdup(s);
|
||||
if (!prv->titles[prv->cur_title]) {
|
||||
return 1;
|
||||
}
|
||||
prv->cur_title++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void csv_output_header(struct view_t *this, const char *title,
|
||||
const char *unit)
|
||||
{
|
||||
static char buff[64];
|
||||
if (unit) {
|
||||
if (csv_is_unit_string(unit)) {
|
||||
snprintf(buff, sizeof(buff), "%s %s", title, unit);
|
||||
} else {
|
||||
snprintf(buff, sizeof(buff), "%s [%s]", title, unit);
|
||||
}
|
||||
csv_output_column(this, buff);
|
||||
} else {
|
||||
csv_output_column(this, title);
|
||||
}
|
||||
}
|
||||
|
||||
static void csv_finish_record(struct view_t *this)
|
||||
{
|
||||
struct csv_out_prv *prv = this->ctx.csv_prv;
|
||||
int i;
|
||||
|
||||
if (prv->column) {
|
||||
putc('\n', this->outfile);
|
||||
}
|
||||
|
||||
/*
|
||||
* For first record we need to output stored data values
|
||||
*/
|
||||
if (csv_is_first_record(this)) {
|
||||
prv->column = 0;
|
||||
for (i = 0; i < prv->cur_val; ++i) {
|
||||
csv_output_column(this, prv->vals[i]);
|
||||
}
|
||||
if (prv->column) {
|
||||
putc('\n', this->outfile);
|
||||
}
|
||||
}
|
||||
fflush(this->outfile);
|
||||
}
|
||||
|
||||
static void csv_free_vals(struct view_t *this)
|
||||
{
|
||||
struct csv_out_prv *prv = this->ctx.csv_prv;
|
||||
int i;
|
||||
|
||||
if (prv->vals) {
|
||||
for (i = 0; i < prv->cur_val; ++i) {
|
||||
free(prv->vals[i]);
|
||||
}
|
||||
free(prv->vals);
|
||||
prv->vals = NULL;
|
||||
prv->cur_val = 0;
|
||||
prv->max_vals = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void csv_free_titles(struct view_t *this)
|
||||
{
|
||||
struct csv_out_prv *prv = this->ctx.csv_prv;
|
||||
int i;
|
||||
|
||||
if (prv->titles) {
|
||||
for (i = 0; i < prv->cur_title; ++i) {
|
||||
free(prv->titles[i]);
|
||||
}
|
||||
free(prv->titles);
|
||||
prv->titles = NULL;
|
||||
prv->cur_title = 0;
|
||||
prv->max_titles = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int csv_process_row(struct view_t *this, int type, int num_fields, char *fields[])
|
||||
{
|
||||
int i;
|
||||
struct csv_out_prv *prv = this->ctx.csv_prv;
|
||||
const char *unit = NULL;
|
||||
|
||||
switch (type) {
|
||||
case DATA_SET:
|
||||
if (prv->record) {
|
||||
csv_finish_record(this);
|
||||
}
|
||||
csv_free_titles(this);
|
||||
csv_free_vals(this);
|
||||
if (prv->data_set) {
|
||||
putc('\n', this->outfile);
|
||||
}
|
||||
if (num_fields > 0) {
|
||||
fprintf(this->outfile, "%s\n", fields[0]);
|
||||
}
|
||||
prv->record = 0;
|
||||
prv->data_set++;
|
||||
break;
|
||||
case RECORD:
|
||||
if (prv->record) {
|
||||
csv_finish_record(this);
|
||||
}
|
||||
prv->column = 0;
|
||||
prv->record++;
|
||||
break;
|
||||
|
||||
/*
|
||||
* For KV pair assume that values are interleaved
|
||||
* with units, so output every second value,
|
||||
* and use units to construct column headers.
|
||||
* For example:
|
||||
* KV_PAIR,Cache Size,10347970,[4KiB blocks],39.47,[GiB]
|
||||
* will result in:
|
||||
* data row: 10347970,39.47
|
||||
* header row: Cache Size [4KiB blocks],Cache Size [GiB]
|
||||
*/
|
||||
case KV_PAIR:
|
||||
for (i = 1; i < num_fields; i += 2) {
|
||||
if (csv_is_first_record(this)) {
|
||||
if (i + 1 < num_fields) {
|
||||
csv_output_header(this, fields[0],
|
||||
fields[i+1]);
|
||||
} else {
|
||||
csv_output_header(this, fields[0], NULL);
|
||||
}
|
||||
}
|
||||
if (csv_output_data(this, fields[i])) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
/*
|
||||
* For table rows assume the following format:
|
||||
* TABLE_{ROW,SECTION},Title,value1,value2,value3,...,unit
|
||||
* This will result in:
|
||||
* data row: value1,value2,value3,...
|
||||
* header row: Title [unit],Title [col1_title],Title [col2_title],...
|
||||
*/
|
||||
case TABLE_HEADER:
|
||||
csv_free_titles(this);
|
||||
csv_add_column_subtitle(this, "");
|
||||
for (i = 2; i < num_fields; i++) {
|
||||
if (csv_add_column_subtitle(this, fields[i])) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TABLE_SECTION:
|
||||
case TABLE_ROW:
|
||||
if (csv_is_first_record(this)) {
|
||||
unit = NULL;
|
||||
if (csv_is_unit_string(fields[num_fields-1])) {
|
||||
unit = fields[num_fields-1];
|
||||
}
|
||||
csv_output_header(this, fields[0], unit);
|
||||
for (i = 2; i < num_fields; i++) {
|
||||
if (!csv_is_unit_string(prv->titles[i-1])) {
|
||||
csv_output_header(this, fields[0],
|
||||
prv->titles[i-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 1; i < num_fields; i++) {
|
||||
if (!csv_is_unit_string(prv->titles[i-1])) {
|
||||
if (csv_output_data(this, fields[i])) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int csv_end_input(struct view_t *this)
|
||||
{
|
||||
csv_finish_record(this);
|
||||
return 0;
|
||||
}
|
||||
int csv_construct(struct view_t *this)
|
||||
{
|
||||
struct csv_out_prv *prv = calloc(sizeof(struct csv_out_prv), 1);
|
||||
|
||||
if (!prv) {
|
||||
return 1;
|
||||
}
|
||||
this->ctx.csv_prv = prv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int csv_destruct(struct view_t *this)
|
||||
{
|
||||
csv_free_vals(this);
|
||||
csv_free_titles(this);
|
||||
free(this->ctx.csv_prv);
|
||||
return 0;
|
||||
}
|
||||
|
18
casadm/statistics_view_csv.h
Normal file
18
casadm/statistics_view_csv.h
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef __STATS_VIEW_CSV
|
||||
#define __STATS_VIEW_CSV
|
||||
|
||||
int csv_process_row(struct view_t *this, int type, int num_fields, char *fields[]);
|
||||
|
||||
int csv_end_input(struct view_t *this);
|
||||
|
||||
int csv_construct(struct view_t *this);
|
||||
|
||||
int csv_destruct(struct view_t *this);
|
||||
|
||||
|
||||
#endif
|
49
casadm/statistics_view_raw_csv.c
Normal file
49
casadm/statistics_view_raw_csv.c
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "statistics_view.h"
|
||||
#include "statistics_view_structs.h"
|
||||
#include "statistics_view_raw_csv.h"
|
||||
|
||||
#define VALS_BUFFER_INIT_SIZE 10
|
||||
|
||||
int raw_csv_process_row(struct view_t *this, int type, int num_fields, char *fields[])
|
||||
{
|
||||
int i;
|
||||
if (RECORD != type && DATA_SET != type) {
|
||||
for (i = 0; i < num_fields; i++) {
|
||||
if (i) {
|
||||
fputc(',', this->outfile);
|
||||
}
|
||||
if (strstr(fields[i], ",")) {
|
||||
fprintf(this->outfile, "\"%s\"", fields[i]);
|
||||
} else {
|
||||
fprintf(this->outfile, "%s", fields[i]);
|
||||
}
|
||||
}
|
||||
fputc('\n', this->outfile);
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int raw_csv_end_input(struct view_t *this)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int raw_csv_construct(struct view_t *this)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int raw_csv_destruct(struct view_t *this)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
18
casadm/statistics_view_raw_csv.h
Normal file
18
casadm/statistics_view_raw_csv.h
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef __STATS_VIEW_RAW_CSV
|
||||
#define __STATS_VIEW_RAW_CSV
|
||||
|
||||
int raw_csv_process_row(struct view_t *this, int type, int num_fields, char *fields[]);
|
||||
|
||||
int raw_csv_end_input(struct view_t *this);
|
||||
|
||||
int raw_csv_construct(struct view_t *this);
|
||||
|
||||
int raw_csv_destruct(struct view_t *this);
|
||||
|
||||
|
||||
#endif
|
29
casadm/statistics_view_structs.h
Normal file
29
casadm/statistics_view_structs.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef __STATS_VIEW_S_H
|
||||
#define __STATS_VIEW_S_H
|
||||
|
||||
struct csv_out_prv;
|
||||
|
||||
struct text_out_prv;
|
||||
|
||||
struct view_t
|
||||
{
|
||||
FILE *outfile;
|
||||
union {
|
||||
struct csv_out_prv *csv_prv;
|
||||
struct text_out_prv *text_prv;
|
||||
} ctx;
|
||||
/* type specific init */
|
||||
int (*construct)(struct view_t *this);
|
||||
int (*process_row)(struct view_t *this, int type, int num_fields, char *fields[]);
|
||||
int (*end_input)(struct view_t *this);
|
||||
int (*destruct)(struct view_t *this);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
1025
casadm/statistics_view_text.c
Normal file
1025
casadm/statistics_view_text.c
Normal file
File diff suppressed because it is too large
Load Diff
18
casadm/statistics_view_text.h
Normal file
18
casadm/statistics_view_text.h
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef __STATS_VIEW_TEXT
|
||||
#define __STATS_VIEW_TEXT
|
||||
|
||||
int text_process_row(struct view_t *this, int type, int num_fields, char *fields[]);
|
||||
|
||||
int text_end_input(struct view_t *this);
|
||||
|
||||
int text_construct(struct view_t *this);
|
||||
|
||||
int text_destruct(struct view_t *this);
|
||||
|
||||
|
||||
#endif
|
231
casadm/table.c
Normal file
231
casadm/table.c
Normal file
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "table.h"
|
||||
#include "safeclib/safe_str_lib.h"
|
||||
#include <cas_ioctl_codes.h>
|
||||
|
||||
#define MIN_STR_SIZE 64
|
||||
|
||||
struct table_row
|
||||
{
|
||||
int width;
|
||||
int max_width;
|
||||
char **cells;
|
||||
};
|
||||
|
||||
|
||||
struct table
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int max_height;
|
||||
struct table_row *r;
|
||||
};
|
||||
|
||||
struct table *table_alloc()
|
||||
{
|
||||
struct table *out = malloc(sizeof(*out));
|
||||
if (!out) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
out->width = 0;
|
||||
out->height = 0;
|
||||
out->max_height = 0;
|
||||
|
||||
out->r = 0;
|
||||
return out;
|
||||
}
|
||||
|
||||
void table_free(struct table *t)
|
||||
{
|
||||
int i, j;
|
||||
if (t->r) {
|
||||
for (i = 0 ; i!= t->max_height; ++i) {
|
||||
if (t->r[i].cells) {
|
||||
for (j = 0 ; j!= t->r[i].max_width; ++j) {
|
||||
if (t->r[i].cells[j]) {
|
||||
free(t->r[i].cells[j]);
|
||||
}
|
||||
}
|
||||
free(t->r[i].cells);
|
||||
}
|
||||
}
|
||||
free(t->r);
|
||||
}
|
||||
free(t);
|
||||
}
|
||||
int table_reset(struct table *t)
|
||||
{
|
||||
int i,j;
|
||||
if (t->r) {
|
||||
for (i = 0 ; i!= t->max_height; ++i) {
|
||||
if (t->r[i].cells) {
|
||||
for (j = 0 ; j!= t->r[i].max_width; ++j) {
|
||||
if (t->r[i].cells[j]) {
|
||||
(t->r[i].cells[j])[0] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
t->r[i].width = 0;
|
||||
}
|
||||
}
|
||||
t->width = 0;
|
||||
t->height = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int maxi(int x, int y)
|
||||
{
|
||||
if (x > y) {
|
||||
return x;
|
||||
} else {
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
char *table_get(struct table *t,int y, int x)
|
||||
{
|
||||
static const char * empty="";
|
||||
if (y >= t->height || x >= t->width) {
|
||||
assert(0);
|
||||
abort();
|
||||
return (char*)empty;
|
||||
}
|
||||
|
||||
/* within assigned boundaries but without allocated boundaries */
|
||||
if (y >= t->max_height) {
|
||||
return (char*)empty;
|
||||
}
|
||||
|
||||
if (x >= t->r[y].max_width) {
|
||||
return (char*)empty;
|
||||
}
|
||||
|
||||
if (!t->r[y].cells) {
|
||||
return (char*)empty;
|
||||
}
|
||||
|
||||
if (!t->r[y].cells[x]) {
|
||||
return (char*)empty;
|
||||
}
|
||||
|
||||
return t->r[y].cells[x];
|
||||
}
|
||||
|
||||
int table_set(struct table *t, int y, int x, char *c)
|
||||
{
|
||||
int i;
|
||||
int len = strnlen(c, MAX_STR_LEN);
|
||||
if (len >= MAX_STR_LEN) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* step 1: ensure that space for row y is allocated */
|
||||
if (!t->r) {
|
||||
t->r = calloc(sizeof(struct table_row), y + 1);
|
||||
if (!t->r) {
|
||||
return 1;
|
||||
}
|
||||
t->max_height = y + 1;
|
||||
} else if (t->max_height <= y) {
|
||||
struct table_row *tmp;
|
||||
int new_m_h = t->max_height*2;
|
||||
if (new_m_h <= y) {
|
||||
new_m_h = y+1;
|
||||
}
|
||||
|
||||
tmp = realloc(t->r, sizeof(struct table_row)*new_m_h);
|
||||
if (!tmp) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
t->r=tmp;
|
||||
for (i = t->max_height; i!= new_m_h; ++i) {
|
||||
t->r[i].width = t->r[i].max_width = 0;
|
||||
t->r[i].cells = 0;
|
||||
}
|
||||
t->max_height = new_m_h;
|
||||
|
||||
} /* else everything is OK */
|
||||
|
||||
/* step 2: ensure that column x within row y is allocated */
|
||||
if (!t->r[y].cells) {
|
||||
t->r[y].cells = calloc(sizeof(char*), x + 1);
|
||||
t->r[y].max_width = x + 1;
|
||||
} else if (t->r[y].max_width <= x) {
|
||||
char **tmp;
|
||||
int new_m_w = t->r[y].max_width*2;
|
||||
if (new_m_w <= x) {
|
||||
new_m_w = x+1;
|
||||
}
|
||||
|
||||
tmp = realloc(t->r[y].cells, sizeof(char*)*new_m_w);
|
||||
if (!tmp) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
t->r[y].cells = tmp;
|
||||
memset(&tmp[t->r[y].max_width], 0,
|
||||
sizeof(char*)*(new_m_w-t->r[y].max_width));
|
||||
t->r[y].max_width = new_m_w;
|
||||
}
|
||||
|
||||
/* step 3: allocate space for string to be contained in cell */
|
||||
if (t->r[y].cells[x] && len+1>MIN_STR_SIZE) {
|
||||
char *tmp = realloc(t->r[y].cells[x], len+1);
|
||||
if (!tmp) {
|
||||
return 1;
|
||||
}
|
||||
t->r[y].cells[x] = tmp;
|
||||
|
||||
} else if (!t->r[y].cells[x]){
|
||||
t->r[y].cells[x] = malloc(maxi(MIN_STR_SIZE,len+1));
|
||||
if (!t->r[y].cells[x]) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* step 4: actually overwrite contents of a cell */
|
||||
strncpy_s(t->r[y].cells[x], len + 1, c, len);
|
||||
|
||||
/* step 5: update width and height of a table */
|
||||
|
||||
t->height = maxi(t->height, y + 1);
|
||||
t->width = maxi(t->width, x + 1);
|
||||
t->r[y].width = maxi(t->r[y].width, x + 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* get last available row of table that was added either via
|
||||
*/
|
||||
int table_get_width(struct table *t)
|
||||
{
|
||||
return t->width;
|
||||
}
|
||||
|
||||
int table_get_height(struct table *t)
|
||||
{
|
||||
return t->height;
|
||||
}
|
||||
|
||||
int table_set_height(struct table *t, int h)
|
||||
{
|
||||
t->height = h;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int table_set_width(struct table *t, int h)
|
||||
{
|
||||
t->width = h;
|
||||
return 0;
|
||||
}
|
||||
|
58
casadm/table.h
Normal file
58
casadm/table.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __TABLE_H
|
||||
#define __TABLE_H
|
||||
|
||||
struct table;
|
||||
|
||||
/**
|
||||
* setup "table" structure.
|
||||
*/
|
||||
struct table *table_alloc();
|
||||
|
||||
/**
|
||||
* deallocate table.
|
||||
*/
|
||||
void table_free(struct table *t);
|
||||
|
||||
/**
|
||||
* max value of two integers
|
||||
*/
|
||||
int maxi(int x, int y);
|
||||
|
||||
/**
|
||||
* retrieve a field of a table
|
||||
*/
|
||||
char *table_get(struct table *t,int y, int x);
|
||||
|
||||
int table_set(struct table *t, int y, int x, char *c);
|
||||
|
||||
/**
|
||||
* reduce number of columns and rows to 0;
|
||||
*/
|
||||
int table_reset(struct table *t);
|
||||
|
||||
/**
|
||||
* get last available column of table that was added via table_set
|
||||
*/
|
||||
int table_get_width(struct table *t);
|
||||
|
||||
/**
|
||||
* get last available row of table that was added either via table_set or table_set_height
|
||||
*/
|
||||
int table_get_height(struct table *t);
|
||||
|
||||
/**
|
||||
* set height of a table (additional rows will contain empty strings
|
||||
*/
|
||||
int table_set_height(struct table *t, int h);
|
||||
/**
|
||||
* set with of a table (additional rows will contain empty strings
|
||||
*/
|
||||
int table_set_width(struct table *t, int h);
|
||||
|
||||
#endif
|
40
casadm/upgrade.c
Normal file
40
casadm/upgrade.c
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include "cas_lib.h"
|
||||
#include "cas_lib_utils.h"
|
||||
#include <cas_ioctl_codes.h>
|
||||
|
||||
extern cas_printf_t cas_printf;
|
||||
|
||||
int upgrade_start()
|
||||
{
|
||||
int fd;
|
||||
struct kcas_upgrade cmd_info;
|
||||
|
||||
if ((fd = open_ctrl_device()) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (run_ioctl_interruptible(fd, KCAS_IOCTL_UPGRADE, &cmd_info,
|
||||
"Starting upgrade", 0, OCF_CORE_ID_INVALID) < 0) {
|
||||
close(fd);
|
||||
if (OCF_ERR_FLUSHING_INTERRUPTED == cmd_info.ext_err_code) {
|
||||
return INTERRUPTED;
|
||||
} else {
|
||||
cas_printf(LOG_ERR, "Error starting upgrade\n");
|
||||
print_err(cmd_info.ext_err_code);
|
||||
return FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return SUCCESS;
|
||||
}
|
11
casadm/upgrade.h
Normal file
11
casadm/upgrade.h
Normal file
@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef _UPGRADE_H
|
||||
#define _UPGRADE_H
|
||||
|
||||
int upgrade_start();
|
||||
|
||||
#endif
|
119
casadm/vt100codes.h
Normal file
119
casadm/vt100codes.h
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
/* General setup */
|
||||
#define RESET_DEVICE "\033c"
|
||||
//! enable line wrapping
|
||||
#define ENABLE_LINE_WRAP "\x1b[7h"
|
||||
//! disable it
|
||||
#define DISABLE_LINE_WRAP "\x1b[7l"
|
||||
|
||||
/* Scrolling options. Note: there is no way to disable scrolling */
|
||||
//! Whole screen is scrolled on SCROLL_UP/SCROLL_DOWN
|
||||
#define SCROLL_ENTIRE_SCREEN "\x1b[r"
|
||||
//! Only rows from A to B are scrolled on SCROLL_UP/SCROLL_DOWN, anything above A or below B is not scrolled
|
||||
#define SCROLL_SCREEN_REGION(A,B) "\x1b[" (A) ";" (B) "r"
|
||||
|
||||
//! scroll up
|
||||
#define SCROLL_UP "\x1b[M"
|
||||
//! scroll down
|
||||
#define SCROLL_DOWN "\x1b[D"
|
||||
|
||||
//! make cursor invisible - xterm
|
||||
#define HIDE_CURSOR "\x1b[?25l"
|
||||
|
||||
//! restore it -xterm
|
||||
#define SHOW_CURSOR "\x1b[?25h"
|
||||
|
||||
/* Absolute cursor positioning. */
|
||||
//! Set cursor position to left-top position
|
||||
#define CURSOR_HOME "\x1b[H"
|
||||
//! Set cursor position to specific y/x (note: y = 1..height, x = 1..width)
|
||||
#define CURSOR_YX "\x1b[%d;%dH"
|
||||
/* Relative cursor positioning. */
|
||||
//! move cursor one position up
|
||||
#define CURSOR_UP "\x1b[A"
|
||||
//! move cursor n positions up
|
||||
#define CURSOR_UP_N "\x1b[%dA"
|
||||
//! move cursor one position down
|
||||
#define CURSOR_DOWN "\x1b[B"
|
||||
//! move cursor n positions down
|
||||
#define CURSOR_DOWN_N "\x1b[%dB"
|
||||
//! move cursor one position forward
|
||||
#define CURSOR_FORWARD "\x1b[C"
|
||||
//! move cursor n positions forward
|
||||
#define CURSOR_FORWARD_N "\x1b[%dC"
|
||||
//! move cursor one position backward
|
||||
#define CURSOR_BACKWARD "\x1b[D"
|
||||
//! move cursor n positions backward
|
||||
#define CURSOR_BACKWARD_N "\x1b[%dD"
|
||||
/* Unsave restores position after last save. */
|
||||
//! One cursor position may be saved
|
||||
#define SAVE_CURSOR "\x1b[s"
|
||||
//! and restored
|
||||
#define UNSAVE_CURSOR "\x1b[u"
|
||||
|
||||
/* Erase screen. */
|
||||
//! Erase whole screen
|
||||
#define ERASE "\x1b[2J"
|
||||
//! same as above
|
||||
#define ERASE_SCREEN ERASE
|
||||
//! erase above cursor
|
||||
#define ERASE_UP "\x1b[1J"
|
||||
//! erase below cursor
|
||||
#define ERASE_DOWN "\x1b[J"
|
||||
|
||||
|
||||
#define INSERT_MODE "\x1b[4h"
|
||||
#define REPLACE_MODE "\x1b[4l"
|
||||
/* Erase line. */
|
||||
//! erase current line
|
||||
#define ERASE_LINE "\x1b[K"
|
||||
//! erase current line left from the cursor
|
||||
#define ERASE_START_OF_LINE "\x1b[1K"
|
||||
//! erase current line right from the cursor
|
||||
#define ERASE_END_OF_LINE "\x1b[K"
|
||||
|
||||
/* a = one of following 23 attributes*/
|
||||
//! set specific attribute
|
||||
#define SET_ATTR "\x1b[%dm"
|
||||
//! if you have to set more attributes, separate them by ";"
|
||||
#define AND_ATTR ";"
|
||||
/*generalattributes (0-8 without 3 and 6) */
|
||||
//!resets terminal defaults
|
||||
#define ATTR_RESET 0
|
||||
//!sets brighter fg color
|
||||
#define ATTR_BRIGHT 1
|
||||
//!turns off bright (sets darker fg color) note: not supported by most of platforms
|
||||
#define ATTR_DIM 2
|
||||
//!turns on text underline (not supported by MS Windows)
|
||||
#define ATTR_UNDERSCORE 4
|
||||
//!turns on blink (Not supported by MS Windows, most of other implementations incompatible)
|
||||
#define ATTR_BLINK 5
|
||||
//! Inverts bg and fg color (incompatible implementation on MS windows)*/
|
||||
#define ATTR_REVERSE 7
|
||||
|
||||
#define ATTR_HIDDEN 8 /*???*/
|
||||
|
||||
/*Foreground (text) colours*/
|
||||
#define FG_COLOR_BLACK 30
|
||||
#define FG_COLOR_RED 31
|
||||
#define FG_COLOR_GREEN 32
|
||||
#define FG_COLOR_YELLOW 33
|
||||
#define FG_COLOR_BLUE 34
|
||||
#define FG_COLOR_MAGENTA 35
|
||||
#define FG_COLOR_CYAN 36
|
||||
#define FG_COLOR_WHITE 37
|
||||
|
||||
/*Background colors*/
|
||||
#define BG_COLOR_BLACK 40
|
||||
#define BG_COLOR_RED 41
|
||||
#define BG_COLOR_GREEN 42
|
||||
#define BG_COLOR_YELLOW 43
|
||||
#define BG_COLOR_BLUE 44
|
||||
#define BG_COLOR_MAGENTA 45
|
||||
#define BG_COLOR_CYAN 46
|
||||
#define BG_COLOR_WHITE 47
|
||||
|
51
modules/CAS_VERSION_GEN
Executable file
51
modules/CAS_VERSION_GEN
Executable file
@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright(c) 2012-2019 Intel Corporation
|
||||
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
#
|
||||
|
||||
VER_FILE=CAS_VERSION
|
||||
|
||||
which git > /dev/null 2>&1
|
||||
if [ $? -eq 0 ] && [ -e ../../../.git ]; then
|
||||
echo "Generating ${VER_FILE} from git revision."
|
||||
echo ""
|
||||
VERSION=`git describe HEAD 2>/dev/null`
|
||||
|
||||
CAS_VERSION_MAIN=`echo ${VERSION} | cut -d '.' -f 1 | awk '{print substr($0, 2)}'`
|
||||
CAS_VERSION_MAJOR=`echo ${VERSION} | cut -d '.' -f 2 | awk '{print substr($0, 2)}'`
|
||||
CAS_VERSION_MINOR=`echo ${VERSION} | cut -d '.' -f 3 | awk '{print substr($0, 2)}'`
|
||||
CAS_BUILD_NO=`echo ${VERSION} | cut -d '.' -f 4 | cut -d '-' -f 1`
|
||||
CAS_BUILD_FLAG=`echo ${VERSION} | cut -d '.' -f 4 | cut -s -d '-' -f 3`
|
||||
|
||||
rm -f ${VER_FILE}
|
||||
touch ${VER_FILE}
|
||||
|
||||
echo "CAS_VERSION_MAIN=${CAS_VERSION_MAIN}" >> ${VER_FILE}
|
||||
echo "CAS_VERSION_MAJOR=${CAS_VERSION_MAJOR}" >> ${VER_FILE}
|
||||
echo "CAS_VERSION_MINOR=${CAS_VERSION_MINOR}" >> ${VER_FILE}
|
||||
echo "CAS_BUILD_NO=${CAS_BUILD_NO}" >> ${VER_FILE}
|
||||
echo "CAS_BUILD_FLAG=${CAS_BUILD_FLAG}" >> ${VER_FILE}
|
||||
elif [ -f ${VER_FILE} ]; then
|
||||
echo "Using existing ${VER_FILE} version file."
|
||||
echo ""
|
||||
else
|
||||
echo "No ${VER_FILE} found. Preparing default version file."
|
||||
echo ""
|
||||
|
||||
CAS_VERSION_MAIN=19
|
||||
CAS_VERSION_MAJOR=3
|
||||
CAS_VERSION_MINOR=0
|
||||
CAS_BUILD_NO=0000`date +%m%d`
|
||||
CAS_BUILD_FLAG=
|
||||
|
||||
touch ${VER_FILE}
|
||||
|
||||
echo "CAS_VERSION_MAIN=${CAS_VERSION_MAIN}" >> ${VER_FILE}
|
||||
echo "CAS_VERSION_MAJOR=${CAS_VERSION_MAJOR}" >> ${VER_FILE}
|
||||
echo "CAS_VERSION_MINOR=${CAS_VERSION_MINOR}" >> ${VER_FILE}
|
||||
echo "CAS_BUILD_NO=${CAS_BUILD_NO}" >> ${VER_FILE}
|
||||
echo "CAS_BUILD_FLAG=${CAS_BUILD_FLAG}" >> ${VER_FILE}
|
||||
fi
|
||||
|
||||
cat ${VER_FILE}
|
76
modules/Makefile
Normal file
76
modules/Makefile
Normal file
@ -0,0 +1,76 @@
|
||||
#
|
||||
# Copyright(c) 2012-2019 Intel Corporation
|
||||
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
#
|
||||
# If KERNELRELEASE is defined, we've been invoked from the
|
||||
# kernel build system and can use its language.
|
||||
ifneq ($(KERNELRELEASE),)
|
||||
|
||||
include $(M)/config.mk
|
||||
|
||||
obj-y += cas_cache/
|
||||
obj-y += cas_disk/
|
||||
|
||||
# Otherwise we were called directly from the command
|
||||
# line; invoke the kernel build system.
|
||||
else
|
||||
|
||||
VERSION_FILE=$(PWD)/CAS_VERSION
|
||||
|
||||
OCFDIR=$(PWD)/../ocf
|
||||
KERNEL_DIR ?= "/lib/modules/$(shell uname -r)/build"
|
||||
PWD=$(shell pwd)
|
||||
KERNEL_VERSION := $(shell uname -r)
|
||||
MODULES_DIR=/lib/modules/$(KERNEL_VERSION)/extra
|
||||
|
||||
DISK_MODULE = cas_disk
|
||||
CACHE_MODULE = cas_cache
|
||||
|
||||
DEPMOD:=$(shell which depmod)
|
||||
RMMOD :=$(shell which rmmod)
|
||||
MODPROBE:=$(shell which modprobe)
|
||||
|
||||
all: default
|
||||
|
||||
$(VERSION_FILE):
|
||||
./CAS_VERSION_GEN
|
||||
|
||||
# Extra targets and file configuration
|
||||
ifneq ($(wildcard $(PWD)/extra.mk),)
|
||||
include $(PWD)/extra.mk
|
||||
else
|
||||
sync distsync:
|
||||
endif
|
||||
|
||||
default: $(VERSION_FILE) sync
|
||||
cd $(KERNEL_DIR) && $(MAKE) M=$(PWD) modules
|
||||
|
||||
clean:
|
||||
cd $(KERNEL_DIR) && make M=$(PWD) clean
|
||||
|
||||
distclean: clean distsync
|
||||
|
||||
install:
|
||||
@echo "Installing Open-CAS modules"
|
||||
@install -m 755 -d $(MODULES_DIR)
|
||||
@install -m 744 cas_disk/$(DISK_MODULE).ko $(MODULES_DIR)/$(DISK_MODULE).ko
|
||||
@install -m 744 cas_cache/$(CACHE_MODULE).ko $(MODULES_DIR)/$(CACHE_MODULE).ko
|
||||
|
||||
@$(DEPMOD)
|
||||
@$(MODPROBE) $(CACHE_MODULE)
|
||||
|
||||
uninstall:
|
||||
@echo "Uninstalling Open-CAS modules"
|
||||
@$(RMMOD) $(CACHE_MODULE)
|
||||
@$(RMMOD) $(DISK_MODULE)
|
||||
|
||||
@rm $(MODULES_DIR)/$(CACHE_MODULE).ko
|
||||
@rm $(MODULES_DIR)/$(DISK_MODULE).ko
|
||||
|
||||
@$(DEPMOD)
|
||||
|
||||
reinstall: uninstall install
|
||||
|
||||
.PHONY: all default clean distclean sync distsync install uninstall
|
||||
|
||||
endif
|
17
modules/README
Normal file
17
modules/README
Normal file
@ -0,0 +1,17 @@
|
||||
Open CAS accelerates Linux applications by caching active (hot) data to
|
||||
a local flash device inside servers. Open CAS implements caching at the
|
||||
server level, utilizing local high-performance flash media as the cache drive
|
||||
media inside the application server as close as possible to the CPU, thus
|
||||
reducing storage latency as much as possible.
|
||||
The Open Cache Acceleration Software installs into the GNU/Linux operating
|
||||
system itself, as a kernel module. The nature of the integration provides a
|
||||
cache solution that is transparent to users and applications, and your
|
||||
existing storage infrastructure. No storage migration effort or application
|
||||
changes are required.
|
||||
|
||||
Open CAS is distributed on Dual BSD-2-Clause-Patent/GPLv2 license (see
|
||||
https://opensource.org/licenses/BSDplusPatent and
|
||||
https://opensource.org/licenses/GPL-2.0 for for full license texts).
|
||||
|
||||
Open CAS uses Safe string library (safeclib) that is MIT licensed.
|
||||
|
3
modules/cas_cache/.gitignore
vendored
Normal file
3
modules/cas_cache/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
include/
|
||||
src/
|
||||
|
10
modules/cas_cache/Makefile
Normal file
10
modules/cas_cache/Makefile
Normal file
@ -0,0 +1,10 @@
|
||||
#
|
||||
# Copyright(c) 2012-2019 Intel Corporation
|
||||
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
#
|
||||
include $(M)/config.mk
|
||||
|
||||
obj-m := cas_cache.o
|
||||
|
||||
cas_cache-c = $(shell find $(M)/cas_cache -name \*.c)
|
||||
cas_cache-objs = $(patsubst $(M)/cas_cache/%.c,%.o,$(cas_cache-c))
|
97
modules/cas_cache/cas_cache.h
Normal file
97
modules/cas_cache/cas_cache.h
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef __CAS_CACHE_H__
|
||||
#define __CAS_CACHE_H__
|
||||
|
||||
#include "ocf/ocf.h"
|
||||
#include "ocf_env.h"
|
||||
|
||||
#include <cas_version.h>
|
||||
#include <cas_ioctl_codes.h>
|
||||
|
||||
#include "linux_kernel_version.h"
|
||||
#include "layer_upgrade.h"
|
||||
#include "control.h"
|
||||
#include "layer_cache_management.h"
|
||||
#include "service_ui_ioctl.h"
|
||||
#include "utils/cas_cache_utils.h"
|
||||
#include "volume/vol_blk_utils.h"
|
||||
#include "classifier.h"
|
||||
#include "context.h"
|
||||
#include <linux/kallsyms.h>
|
||||
|
||||
#define CAS_KERN_EMERG KERN_EMERG OCF_PREFIX_SHORT
|
||||
#define CAS_KERN_ALERT KERN_ALERT OCF_PREFIX_SHORT
|
||||
#define CAS_KERN_CRIT KERN_CRIT OCF_PREFIX_SHORT
|
||||
#define CAS_KERN_ERR KERN_ERR OCF_PREFIX_SHORT
|
||||
#define CAS_KERN_WARNING KERN_WARNING OCF_PREFIX_SHORT
|
||||
#define CAS_KERN_NOTICE KERN_NOTICE OCF_PREFIX_SHORT
|
||||
#define CAS_KERN_INFO KERN_INFO OCF_PREFIX_SHORT
|
||||
#define CAS_KERN_DEBUG KERN_DEBUG OCF_PREFIX_SHORT
|
||||
|
||||
#ifndef SECTOR_SHIFT
|
||||
#define SECTOR_SHIFT 9
|
||||
#endif
|
||||
|
||||
#ifndef SECTOR_SIZE
|
||||
#define SECTOR_SIZE (1<<SECTOR_SHIFT)
|
||||
#endif
|
||||
|
||||
#define MAX_LINES_PER_IO 16
|
||||
|
||||
/**
|
||||
* cache/core object types */
|
||||
enum {
|
||||
BLOCK_DEVICE_VOLUME = 1, /**< block device volume */
|
||||
ATOMIC_DEVICE_VOLUME, /**< block device volume with atomic
|
||||
metadata support */
|
||||
/** \cond SKIP_IN_DOC */
|
||||
OBJECT_TYPE_MAX,
|
||||
NVME_CONTROLLER
|
||||
/** \endcond */
|
||||
};
|
||||
|
||||
struct cas_classifier;
|
||||
|
||||
struct cache_priv {
|
||||
struct cas_classifier *classifier;
|
||||
ocf_queue_t mngt_queue;
|
||||
ocf_queue_t io_queues[];
|
||||
};
|
||||
|
||||
extern ocf_ctx_t cas_ctx;
|
||||
|
||||
extern struct casdsk_functions_mapper casdisk_functions;
|
||||
|
||||
struct casdsk_functions_mapper {
|
||||
int (*casdsk_disk_dettach)(struct casdsk_disk *dsk);
|
||||
int (*casdsk_exp_obj_destroy)(struct casdsk_disk *dsk);
|
||||
int (*casdsk_exp_obj_create)(struct casdsk_disk *dsk, const char *dev_name,
|
||||
struct module *owner, struct casdsk_exp_obj_ops *ops);
|
||||
struct request_queue *(*casdsk_disk_get_queue)(struct casdsk_disk *dsk);
|
||||
void (*casdsk_store_config)(size_t n_blobs, struct casdsk_props_conf *blobs);
|
||||
struct block_device *(*casdsk_disk_get_blkdev)(struct casdsk_disk *dsk);
|
||||
struct request_queue *(*casdsk_exp_obj_get_queue)(struct casdsk_disk *dsk);
|
||||
uint32_t (*casdsk_get_version)(void);
|
||||
void (*casdsk_disk_close)(struct casdsk_disk *dsk);
|
||||
struct casdsk_disk *(*casdsk_disk_claim)(const char *path, void *private);
|
||||
int (*casdsk_exp_obj_unlock)(struct casdsk_disk *dsk);
|
||||
int (*casdsk_disk_set_pt)(struct casdsk_disk *dsk);
|
||||
size_t (*casdsk_get_stored_config)(struct casdsk_props_conf **blobs);
|
||||
struct gendisk *(*casdsk_disk_get_gendisk)(struct casdsk_disk *dsk);
|
||||
int (*casdsk_disk_attach) (struct casdsk_disk *dsk, struct module *owner,
|
||||
struct casdsk_exp_obj_ops *ops);
|
||||
int (*casdsk_disk_set_attached)(struct casdsk_disk *dsk);
|
||||
int (*casdsk_exp_obj_activate)(struct casdsk_disk *dsk);
|
||||
bool (*casdsk_exp_obj_activated)(struct casdsk_disk *ds);
|
||||
int (*casdsk_exp_obj_lock)(struct casdsk_disk *dsk);
|
||||
void (*casdsk_free_stored_config)(void);
|
||||
struct casdsk_disk *(*casdsk_disk_open)(const char *path, void *private);
|
||||
int (*casdsk_disk_clear_pt)(struct casdsk_disk *dsk);
|
||||
struct gendisk *(*casdsk_exp_obj_get_gendisk)(struct casdsk_disk *dsk);
|
||||
};
|
||||
|
||||
#endif
|
967
modules/cas_cache/classifier.c
Normal file
967
modules/cas_cache/classifier.c
Normal file
@ -0,0 +1,967 @@
|
||||
/*
|
||||
* Copyright(c) 2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "cas_cache.h"
|
||||
#include "linux_kernel_version.h"
|
||||
#include "classifier.h"
|
||||
#include "classifier_defs.h"
|
||||
#include <linux/namei.h>
|
||||
|
||||
/* Kernel log prefix */
|
||||
#define CAS_CLS_LOG_PREFIX OCF_PREFIX_SHORT"[Classifier]"
|
||||
|
||||
/* Production version logs */
|
||||
#define CAS_CLS_MSG(severity, format, ...) \
|
||||
printk(severity CAS_CLS_LOG_PREFIX " " format, ##__VA_ARGS__);
|
||||
|
||||
/* Set to 1 to enable debug logs */
|
||||
#define CAS_CLASSIFIER_CLS_DEBUG 0
|
||||
|
||||
#if 1 == CAS_CLASSIFIER_CLS_DEBUG
|
||||
/* Debug log */
|
||||
#define CAS_CLS_DEBUG_MSG(format, ...) \
|
||||
CAS_CLS_MSG(KERN_INFO, format, ##__VA_ARGS__)
|
||||
/* Trace log */
|
||||
#define CAS_CLS_DEBUG_TRACE(format, ...) \
|
||||
trace_printk(format, ##__VA_ARGS__)
|
||||
|
||||
#else
|
||||
#define CAS_CLS_DEBUG_MSG(format, ...)
|
||||
#define CAS_CLS_DEBUG_TRACE(format, ...)
|
||||
#endif
|
||||
|
||||
/* Done condition test - always accepts and stops evaluation */
|
||||
static cas_cls_eval_t _cas_cls_done_test(struct cas_classifier *cls,
|
||||
struct cas_cls_condition *c, struct cas_cls_io *io,
|
||||
ocf_part_id_t part_id)
|
||||
{
|
||||
cas_cls_eval_t ret = {.yes = 1, .stop = 1};
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Metadata condition test */
|
||||
static cas_cls_eval_t _cas_cls_metadata_test(struct cas_classifier *cls,
|
||||
struct cas_cls_condition *c, struct cas_cls_io *io,
|
||||
ocf_part_id_t part_id)
|
||||
{
|
||||
if (!io->page)
|
||||
return cas_cls_eval_no;
|
||||
|
||||
if (PageAnon(io->page))
|
||||
return cas_cls_eval_no;
|
||||
|
||||
if (PageSlab(io->page) || PageCompound(io->page)) {
|
||||
/* A filesystem issues IO on pages that does not belongs
|
||||
* to the file page cache. It means that it is a
|
||||
* part of metadata
|
||||
*/
|
||||
return cas_cls_eval_yes;
|
||||
}
|
||||
|
||||
if (!io->page->mapping) {
|
||||
/* XFS case, page are allocated internally and do not
|
||||
* have references into inode
|
||||
*/
|
||||
return cas_cls_eval_yes;
|
||||
}
|
||||
|
||||
if (!io->inode)
|
||||
return cas_cls_eval_no;
|
||||
|
||||
if (S_ISBLK(io->inode->i_mode)) {
|
||||
/* EXT3 and EXT4 case. Metadata IO is performed into pages
|
||||
* of block device cache
|
||||
*/
|
||||
return cas_cls_eval_yes;
|
||||
}
|
||||
|
||||
if (S_ISDIR(io->inode->i_mode)) {
|
||||
return cas_cls_eval_yes;
|
||||
}
|
||||
|
||||
return cas_cls_eval_no;
|
||||
}
|
||||
|
||||
/* Direct I/O condition test function */
|
||||
static cas_cls_eval_t _cas_cls_direct_test(struct cas_classifier *cls,
|
||||
struct cas_cls_condition *c, struct cas_cls_io *io,
|
||||
ocf_part_id_t part_id)
|
||||
{
|
||||
if (!io->page)
|
||||
return cas_cls_eval_no;
|
||||
|
||||
if (PageAnon(io->page))
|
||||
return cas_cls_eval_yes;
|
||||
|
||||
return cas_cls_eval_no;
|
||||
}
|
||||
|
||||
/* Generic condition constructor for conditions without operands (e.g. direct,
|
||||
* metadata) */
|
||||
static int _cas_cls_generic_ctr(struct cas_classifier *cls,
|
||||
struct cas_cls_condition *c, char *data)
|
||||
{
|
||||
if (data) {
|
||||
CAS_CLS_MSG(KERN_ERR, "Unexpected operand in condition\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Generic condition destructor */
|
||||
static void _cas_cls_generic_dtr(struct cas_classifier *cls,
|
||||
struct cas_cls_condition *c)
|
||||
{
|
||||
if (c->context)
|
||||
kfree(c->context);
|
||||
c->context = NULL;
|
||||
}
|
||||
|
||||
/* Numeric condition constructor. @data is expected to contain either
|
||||
* plain number string or range specifier (e.g. "gt:4096"). */
|
||||
static int _cas_cls_numeric_ctr(struct cas_classifier* cls,
|
||||
struct cas_cls_condition *c, char *data)
|
||||
{
|
||||
struct cas_cls_numeric *ctx;
|
||||
int result;
|
||||
char *ptr;
|
||||
|
||||
if (!data || strlen(data) == 0) {
|
||||
CAS_CLS_MSG(KERN_ERR, "Missing numeric condition operand\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->operator = cas_cls_numeric_eq;
|
||||
|
||||
ptr = strpbrk(data, ":");
|
||||
if (ptr) {
|
||||
/* Terminate sub-string containing arithmetic operator */
|
||||
*ptr = '\0';
|
||||
++ptr;
|
||||
|
||||
if (!strcmp(data, "eq")) {
|
||||
ctx->operator = cas_cls_numeric_eq;
|
||||
} else if (!strcmp(data, "ne")) {
|
||||
ctx->operator = cas_cls_numeric_ne;
|
||||
} else if (!strcmp(data, "lt")) {
|
||||
ctx->operator = cas_cls_numeric_lt;
|
||||
} else if (!strcmp(data, "gt")) {
|
||||
ctx->operator = cas_cls_numeric_gt;
|
||||
} else if (!strcmp(data, "le")) {
|
||||
ctx->operator = cas_cls_numeric_le;
|
||||
} else if (!strcmp(data, "ge")) {
|
||||
ctx->operator = cas_cls_numeric_ge;
|
||||
} else {
|
||||
CAS_CLS_MSG(KERN_ERR, "Invalid numeric operator \n");
|
||||
result = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* Plain number case */
|
||||
ptr = data;
|
||||
}
|
||||
|
||||
result = kstrtou64(ptr, 10, &ctx->v_u64);
|
||||
if (result) {
|
||||
CAS_CLS_MSG(KERN_ERR, "Invalid numeric operand\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
CAS_CLS_DEBUG_MSG("\t\t - Using operator %d with value %llu\n",
|
||||
ctx->operator, ctx->v_u64);
|
||||
|
||||
c->context = ctx;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
kfree(ctx);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Unsigned int numeric test function */
|
||||
static cas_cls_eval_t _cas_cls_numeric_test_u(
|
||||
struct cas_cls_condition *c, uint64_t val)
|
||||
{
|
||||
struct cas_cls_numeric *ctx = c->context;
|
||||
|
||||
switch (ctx->operator) {
|
||||
case cas_cls_numeric_eq:
|
||||
return val == ctx->v_u64 ? cas_cls_eval_yes : cas_cls_eval_no;
|
||||
case cas_cls_numeric_ne:
|
||||
return val != ctx->v_u64 ? cas_cls_eval_yes : cas_cls_eval_no;
|
||||
case cas_cls_numeric_lt:
|
||||
return val < ctx->v_u64 ? cas_cls_eval_yes : cas_cls_eval_no;
|
||||
case cas_cls_numeric_gt:
|
||||
return val > ctx->v_u64 ? cas_cls_eval_yes : cas_cls_eval_no;
|
||||
case cas_cls_numeric_le:
|
||||
return val <= ctx->v_u64 ? cas_cls_eval_yes : cas_cls_eval_no;
|
||||
case cas_cls_numeric_ge:
|
||||
return val >= ctx->v_u64 ? cas_cls_eval_yes : cas_cls_eval_no;
|
||||
}
|
||||
|
||||
return cas_cls_eval_no;
|
||||
}
|
||||
|
||||
/* Io class test function */
|
||||
static cas_cls_eval_t _cas_cls_io_class_test(struct cas_classifier *cls,
|
||||
struct cas_cls_condition *c, struct cas_cls_io *io,
|
||||
ocf_part_id_t part_id)
|
||||
{
|
||||
|
||||
return _cas_cls_numeric_test_u(c, part_id);
|
||||
}
|
||||
|
||||
/* File size test function */
|
||||
static cas_cls_eval_t _cas_cls_file_size_test(
|
||||
struct cas_classifier *cls, struct cas_cls_condition *c,
|
||||
struct cas_cls_io *io, ocf_part_id_t part_id)
|
||||
{
|
||||
if (!io->inode)
|
||||
return cas_cls_eval_no;
|
||||
|
||||
if (S_ISBLK(io->inode->i_mode))
|
||||
return cas_cls_eval_no;
|
||||
|
||||
if (!S_ISREG(io->inode->i_mode))
|
||||
return cas_cls_eval_no;
|
||||
|
||||
return _cas_cls_numeric_test_u(c, i_size_read(io->inode));
|
||||
}
|
||||
|
||||
/* Resolve path to inode */
|
||||
static void _cas_cls_directory_resolve(struct cas_classifier *cls,
|
||||
struct cas_cls_directory *ctx)
|
||||
{
|
||||
struct path path;
|
||||
struct inode *inode;
|
||||
int error;
|
||||
int o_res;
|
||||
unsigned long o_ino;
|
||||
|
||||
o_res = ctx->resolved;
|
||||
o_ino = ctx->i_ino;
|
||||
|
||||
error = kern_path(ctx->pathname, LOOKUP_FOLLOW, &path);
|
||||
if (error) {
|
||||
ctx->resolved = 0;
|
||||
if (o_res) {
|
||||
CAS_CLS_DEBUG_MSG("Removed inode resolution for %s\n",
|
||||
ctx->pathname);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
inode = path.dentry->d_inode;
|
||||
ctx->i_ino = inode->i_ino;
|
||||
ctx->resolved = 1;
|
||||
path_put(&path);
|
||||
|
||||
if (!o_res) {
|
||||
CAS_CLS_DEBUG_MSG("Resolved %s to inode: %lu\n", ctx->pathname,
|
||||
ctx->i_ino);
|
||||
} else if (o_ino != ctx->i_ino) {
|
||||
CAS_CLS_DEBUG_MSG("Changed inode resolution for %s: %lu => %lu"
|
||||
"\n", ctx->pathname, o_ino, ctx->i_ino);
|
||||
}
|
||||
}
|
||||
|
||||
/* Inode resolving work entry point */
|
||||
static void _cas_cls_directory_resolve_work(struct work_struct *work)
|
||||
{
|
||||
struct cas_cls_directory *ctx;
|
||||
|
||||
ctx = container_of(work, struct cas_cls_directory, d_work.work);
|
||||
|
||||
_cas_cls_directory_resolve(ctx->cls, ctx);
|
||||
|
||||
queue_delayed_work(ctx->cls->wq, &ctx->d_work,
|
||||
msecs_to_jiffies(ctx->resolved ? 5000 : 1000));
|
||||
}
|
||||
|
||||
/* Get unaliased dentry for given dir inode */
|
||||
static struct dentry *_cas_cls_dir_get_inode_dentry(struct inode *inode)
|
||||
{
|
||||
struct dentry *d = NULL, *iter;
|
||||
ALIAS_NODE_TYPE *pos; /* alias list current element */
|
||||
|
||||
if (DENTRY_LIST_EMPTY(&inode->i_dentry))
|
||||
return NULL;
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
goto unlock;
|
||||
|
||||
INODE_FOR_EACH_DENTRY(pos, &inode->i_dentry) {
|
||||
iter = ALIAS_NODE_TO_DENTRY(pos);
|
||||
spin_lock(&iter->d_lock);
|
||||
if (!d_unhashed(iter))
|
||||
d = iter;
|
||||
spin_unlock(&d->d_lock);
|
||||
if (d)
|
||||
break;
|
||||
}
|
||||
|
||||
unlock:
|
||||
spin_unlock(&inode->i_lock);
|
||||
return d;
|
||||
}
|
||||
|
||||
/* Directory condition test function */
|
||||
static cas_cls_eval_t _cas_cls_directory_test(
|
||||
struct cas_classifier *cls, struct cas_cls_condition *c,
|
||||
struct cas_cls_io *io, ocf_part_id_t part_id)
|
||||
{
|
||||
struct cas_cls_directory *ctx;
|
||||
struct inode *inode, *p_inode;
|
||||
struct dentry *dentry, *p_dentry;
|
||||
|
||||
ctx = c->context;
|
||||
inode = io->inode;
|
||||
|
||||
if (!inode || !ctx->resolved)
|
||||
return cas_cls_eval_no;
|
||||
|
||||
/* I/O target inode dentry */
|
||||
dentry = _cas_cls_dir_get_inode_dentry(inode);
|
||||
if (!dentry)
|
||||
return cas_cls_eval_no;
|
||||
|
||||
/* Walk up directory tree starting from I/O destination
|
||||
* dir until current dir inode matches condition inode or top
|
||||
* directory is reached. */
|
||||
while (inode) {
|
||||
if (inode->i_ino == ctx->i_ino)
|
||||
return cas_cls_eval_yes;
|
||||
spin_lock(&dentry->d_lock);
|
||||
p_dentry = dentry->d_parent;
|
||||
if (!p_dentry) {
|
||||
spin_unlock(&dentry->d_lock);
|
||||
return cas_cls_eval_no;
|
||||
}
|
||||
p_inode = p_dentry->d_inode;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
if (p_inode != inode) {
|
||||
inode = p_inode;
|
||||
dentry = p_dentry;
|
||||
} else {
|
||||
inode = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return cas_cls_eval_no;
|
||||
}
|
||||
|
||||
/* Directory condition constructor */
|
||||
static int _cas_cls_directory_ctr(struct cas_classifier *cls,
|
||||
struct cas_cls_condition *c, char *data)
|
||||
{
|
||||
struct cas_cls_directory *ctx;
|
||||
|
||||
if (!data || strlen(data) == 0) {
|
||||
CAS_CLS_MSG(KERN_ERR, "Missing directory specifier\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->cls = cls;
|
||||
ctx->resolved = 0;
|
||||
ctx->pathname = kstrdup(data, GFP_KERNEL);
|
||||
if (!ctx->pathname) {
|
||||
kfree(ctx);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&ctx->d_work, _cas_cls_directory_resolve_work);
|
||||
queue_delayed_work(cls->wq, &ctx->d_work,
|
||||
msecs_to_jiffies(10));
|
||||
|
||||
c->context = ctx;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Directory condition destructor */
|
||||
static void _cas_cls_directory_dtr(struct cas_classifier *cls,
|
||||
struct cas_cls_condition *c)
|
||||
{
|
||||
struct cas_cls_directory *ctx;
|
||||
ctx = c->context;
|
||||
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
cancel_delayed_work_sync(&ctx->d_work);
|
||||
kfree(ctx->pathname);
|
||||
kfree(ctx);
|
||||
}
|
||||
|
||||
/* Array of condition handlers */
|
||||
static struct cas_cls_condition_handler _handlers[] = {
|
||||
{ "done", _cas_cls_done_test, _cas_cls_generic_ctr },
|
||||
{ "metadata", _cas_cls_metadata_test, _cas_cls_generic_ctr },
|
||||
{ "direct", _cas_cls_direct_test, _cas_cls_generic_ctr },
|
||||
{ "io_class", _cas_cls_io_class_test, _cas_cls_numeric_ctr,
|
||||
_cas_cls_generic_dtr },
|
||||
{ "file_size", _cas_cls_file_size_test, _cas_cls_numeric_ctr,
|
||||
_cas_cls_generic_dtr },
|
||||
{ "directory", _cas_cls_directory_test, _cas_cls_directory_ctr,
|
||||
_cas_cls_directory_dtr },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
/* Get condition handler for condition string token */
|
||||
static struct cas_cls_condition_handler *_cas_cls_lookup_handler(
|
||||
const char *token)
|
||||
{
|
||||
struct cas_cls_condition_handler *h = _handlers;
|
||||
|
||||
while (h->token) {
|
||||
if (strcmp(h->token, token) == 0)
|
||||
return h;
|
||||
h++;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Deallocate condition */
|
||||
static void _cas_cls_free_condition(struct cas_classifier *cls,
|
||||
struct cas_cls_condition *c)
|
||||
{
|
||||
if (c->handler->dtr)
|
||||
c->handler->dtr(cls, c);
|
||||
kfree(c);
|
||||
}
|
||||
|
||||
/* Allocate condition */
|
||||
static struct cas_cls_condition * _cas_cls_create_condition(
|
||||
struct cas_classifier *cls, const char *token,
|
||||
char *data, int l_op)
|
||||
{
|
||||
struct cas_cls_condition_handler *h;
|
||||
struct cas_cls_condition *c;
|
||||
int result;
|
||||
|
||||
h = _cas_cls_lookup_handler(token);
|
||||
if (!h) {
|
||||
CAS_CLS_DEBUG_MSG("Cannot find handler for condition"
|
||||
" %s\n", token);
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
|
||||
c = kmalloc(sizeof(*c), GFP_KERNEL);
|
||||
if (!c)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
c->handler = h;
|
||||
c->context = NULL;
|
||||
c->l_op = l_op;
|
||||
|
||||
if (c->handler->ctr) {
|
||||
result = c->handler->ctr(cls, c, data);
|
||||
if (result) {
|
||||
kfree(c);
|
||||
return ERR_PTR(result);
|
||||
}
|
||||
}
|
||||
|
||||
CAS_CLS_DEBUG_MSG("\t\t - Created condition %s\n", token);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Read single codnition from text input and return cas_cls_condition
|
||||
* representation. *rule pointer is advanced to point to next condition.
|
||||
* Input @rule string is modified to speed up parsing (selected bytes are
|
||||
* overwritten with 0).
|
||||
*
|
||||
* *l_op contains logical operator from previous condition and gets overwritten
|
||||
* with operator read from currently parsed condition.
|
||||
*
|
||||
* Returns pointer to condition if successfull.
|
||||
* Returns NULL if no more conditions in string.
|
||||
* Returns error pointer in case of syntax or runtime error.
|
||||
*/
|
||||
static struct cas_cls_condition *_cas_cls_parse_condition(
|
||||
struct cas_classifier *cls, char **rule,
|
||||
enum cas_cls_logical_op *l_op)
|
||||
{
|
||||
char *token = *rule; /* Condition token substring (e.g. file_size) */
|
||||
char *operand = NULL; /* Operand substring (e.g. "lt:4096" or path) */
|
||||
char *ptr; /* Current position in input string */
|
||||
char *last = token; /* Last seen substring in condition */
|
||||
char op = 'X'; /* Logical operator at the end of condition */
|
||||
struct cas_cls_condition *c; /* Output condition */
|
||||
|
||||
if (**rule == '\0') {
|
||||
/* Empty condition */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ptr = strpbrk(*rule, ":&|");
|
||||
if (!ptr) {
|
||||
/* No operands in condition (e.g. "metadata"), no logical
|
||||
* operators following condition - we're done with parsing. */
|
||||
goto create;
|
||||
}
|
||||
|
||||
if (*ptr == ':') {
|
||||
/* Operand found - terminate token string and move forward. */
|
||||
*ptr = '\0';
|
||||
ptr += 1;
|
||||
operand = ptr;
|
||||
last = ptr;
|
||||
|
||||
ptr = strpbrk(ptr, "&|");
|
||||
if (!ptr) {
|
||||
/* No operator past condition - create rule and exit */
|
||||
goto create;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remember operator value and zero target byte to terminate previous
|
||||
* string (token or operand) */
|
||||
op = *ptr;
|
||||
*ptr = '\0';
|
||||
|
||||
create:
|
||||
c = _cas_cls_create_condition(cls, token, operand, *l_op);
|
||||
*l_op = (op == '|' ? cas_cls_logical_or : cas_cls_logical_and);
|
||||
|
||||
/* Set *rule to character past current condition and logical operator */
|
||||
if (ptr) {
|
||||
/* Set pointer for next iteration */
|
||||
*rule = ptr + 1;
|
||||
} else {
|
||||
/* Set pointer to terminating zero */
|
||||
*rule = last + strlen(last);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Parse all conditions in rule text description. @rule might be overwritten */
|
||||
static int _cas_cls_parse_conditions(struct cas_classifier *cls,
|
||||
struct cas_cls_rule *r, char *rule)
|
||||
{
|
||||
char *start;
|
||||
struct cas_cls_condition *c;
|
||||
enum cas_cls_logical_op l_op = cas_cls_logical_or;
|
||||
|
||||
start = rule;
|
||||
for (;;) {
|
||||
c = _cas_cls_parse_condition(cls, &start, &l_op);
|
||||
if (IS_ERR(c))
|
||||
return PTR_ERR(c);
|
||||
if (!c)
|
||||
break;
|
||||
|
||||
list_add_tail(&c->list, &r->conditions);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct cas_classifier* cas_get_classifier(ocf_cache_t cache)
|
||||
{
|
||||
struct cache_priv *cache_priv = ocf_cache_get_priv(cache);
|
||||
ENV_BUG_ON(!cache_priv);
|
||||
return cache_priv->classifier;
|
||||
}
|
||||
|
||||
static void cas_set_classifier(ocf_cache_t cache,
|
||||
struct cas_classifier* cls)
|
||||
{
|
||||
struct cache_priv *cache_priv = ocf_cache_get_priv(cache);
|
||||
ENV_BUG_ON(!cache_priv);
|
||||
cache_priv->classifier = cls;
|
||||
}
|
||||
|
||||
void _cas_cls_rule_destroy(struct cas_classifier *cls,
|
||||
struct cas_cls_rule *r)
|
||||
{
|
||||
struct list_head *item, *n;
|
||||
struct cas_cls_condition *c = NULL;
|
||||
|
||||
if (!r)
|
||||
return;
|
||||
|
||||
list_for_each_safe(item, n, &r->conditions) {
|
||||
c = list_entry(item, struct cas_cls_condition, list);
|
||||
list_del(item);
|
||||
_cas_cls_free_condition(cls, c);
|
||||
}
|
||||
|
||||
kfree(r);
|
||||
}
|
||||
|
||||
/* Destroy rule */
|
||||
void cas_cls_rule_destroy(ocf_cache_t cache, struct cas_cls_rule *r)
|
||||
{
|
||||
struct cas_classifier *cls = cas_get_classifier(cache);
|
||||
BUG_ON(!cls);
|
||||
_cas_cls_rule_destroy(cls, r);
|
||||
}
|
||||
|
||||
/* Create rule from text description. @rule might be overwritten */
|
||||
static struct cas_cls_rule *_cas_cls_rule_create(struct cas_classifier *cls,
|
||||
ocf_part_id_t part_id, char *rule)
|
||||
{
|
||||
struct cas_cls_rule *r;
|
||||
int result;
|
||||
|
||||
if (part_id == 0 || rule[0] == '\0')
|
||||
return NULL;
|
||||
|
||||
r = kmalloc(sizeof(*r), GFP_KERNEL);
|
||||
if (!r)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
r->part_id = part_id;
|
||||
INIT_LIST_HEAD(&r->conditions);
|
||||
result = _cas_cls_parse_conditions(cls, r, rule);
|
||||
if (result) {
|
||||
_cas_cls_rule_destroy(cls, r);
|
||||
return ERR_PTR(result);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Update rule associated with given io class */
|
||||
void cas_cls_rule_apply(ocf_cache_t cache,
|
||||
ocf_part_id_t part_id, struct cas_cls_rule *new)
|
||||
{
|
||||
struct cas_classifier *cls;
|
||||
struct cas_cls_rule *old = NULL, *elem;
|
||||
struct list_head *item, *_n;
|
||||
|
||||
cls = cas_get_classifier(cache);
|
||||
BUG_ON(!cls);
|
||||
|
||||
write_lock(&cls->lock);
|
||||
|
||||
/* Walk through list of rules in reverse order (tail to head), visiting
|
||||
* rules from high to low part_id */
|
||||
list_for_each_prev_safe(item, _n, &cls->rules) {
|
||||
elem = list_entry(item, struct cas_cls_rule, list);
|
||||
|
||||
if (elem->part_id == part_id) {
|
||||
old = elem;
|
||||
list_del(item);
|
||||
}
|
||||
|
||||
if (elem->part_id < part_id)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Insert new element past loop cursor */
|
||||
if (new)
|
||||
list_add(&new->list, item);
|
||||
|
||||
write_unlock(&cls->lock);
|
||||
|
||||
_cas_cls_rule_destroy(cls, old);
|
||||
|
||||
if (old)
|
||||
CAS_CLS_DEBUG_MSG("Removed rule for class %d\n", part_id);
|
||||
if (new)
|
||||
CAS_CLS_DEBUG_MSG("New rule for for class %d\n", part_id);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate classification rule error from linux error code to CAS error code.
|
||||
* Internal classifier functions use PTR_ERR / ERR_PTR macros to propagate
|
||||
* error in pointers. These macros do not work well with CAS error codes, so
|
||||
* this function is used to form fine-grained CAS error code when returning
|
||||
* from classifier management function.
|
||||
*/
|
||||
static int _cas_cls_rule_err_to_cass_err(int err)
|
||||
{
|
||||
switch (err) {
|
||||
case -ENOENT:
|
||||
return KCAS_ERR_CLS_RULE_UNKNOWN_CONDITION;
|
||||
case -EINVAL:
|
||||
return KCAS_ERR_CLS_RULE_INVALID_SYNTAX;
|
||||
default:
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create and apply classification rule for given class id */
|
||||
static int _cas_cls_rule_init(ocf_cache_t cache, ocf_part_id_t part_id)
|
||||
{
|
||||
struct cas_classifier *cls;
|
||||
struct ocf_io_class_info *info;
|
||||
struct cas_cls_rule *r;
|
||||
int result;
|
||||
|
||||
cls = cas_get_classifier(cache);
|
||||
if (!cls)
|
||||
return -EINVAL;
|
||||
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
result = ocf_cache_io_class_get_info(cache, part_id, info);
|
||||
if (result) {
|
||||
if (result == -OCF_ERR_IO_CLASS_NOT_EXIST)
|
||||
result = 0;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (strnlen(info->name, sizeof(info->name)) == sizeof(info->name)) {
|
||||
CAS_CLS_MSG(KERN_ERR, "IO class name not null terminated\n");
|
||||
result = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
r = _cas_cls_rule_create(cls, part_id, info->name);
|
||||
if (IS_ERR(r)) {
|
||||
result = _cas_cls_rule_err_to_cass_err(PTR_ERR(r));
|
||||
goto exit;
|
||||
}
|
||||
|
||||
cas_cls_rule_apply(cache, part_id, r);
|
||||
|
||||
exit:
|
||||
kfree(info);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Create classification rule from text description */
|
||||
int cas_cls_rule_create(ocf_cache_t cache,
|
||||
ocf_part_id_t part_id, const char* rule,
|
||||
struct cas_cls_rule **cls_rule)
|
||||
{
|
||||
struct cas_cls_rule *r = NULL;
|
||||
struct cas_classifier *cls;
|
||||
char *_rule;
|
||||
int ret;
|
||||
|
||||
if (!cls_rule)
|
||||
return -EINVAL;
|
||||
|
||||
cls = cas_get_classifier(cache);
|
||||
if (!cls)
|
||||
return -EINVAL;
|
||||
|
||||
if (strnlen(rule, OCF_IO_CLASS_NAME_MAX) == OCF_IO_CLASS_NAME_MAX) {
|
||||
CAS_CLS_MSG(KERN_ERR, "IO class name not null terminated\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Make description copy as _cas_cls_rule_create might modify input
|
||||
* string */
|
||||
_rule = kstrdup(rule, GFP_KERNEL);
|
||||
if (!_rule)
|
||||
return -ENOMEM;
|
||||
|
||||
r = _cas_cls_rule_create(cls, part_id, _rule);
|
||||
if (IS_ERR(r))
|
||||
ret = _cas_cls_rule_err_to_cass_err(PTR_ERR(r));
|
||||
else {
|
||||
CAS_CLS_DEBUG_MSG("Created rule: %s => %d\n", rule, part_id);
|
||||
*cls_rule = r;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
kfree(_rule);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Deinitialize classifier and remove rules */
|
||||
void cas_cls_deinit(ocf_cache_t cache)
|
||||
{
|
||||
struct cas_classifier *cls;
|
||||
struct list_head *item, *n;
|
||||
struct cas_cls_rule *r = NULL;
|
||||
|
||||
cls = cas_get_classifier(cache);
|
||||
ENV_BUG_ON(!cls);
|
||||
|
||||
list_for_each_safe(item, n, &cls->rules) {
|
||||
r = list_entry(item, struct cas_cls_rule, list);
|
||||
list_del(item);
|
||||
_cas_cls_rule_destroy(cls, r);
|
||||
}
|
||||
|
||||
destroy_workqueue(cls->wq);
|
||||
|
||||
kfree(cls);
|
||||
cas_set_classifier(cache, NULL);
|
||||
|
||||
CAS_CLS_MSG(KERN_INFO, "Deinitialized IO classifier\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialize classifier context */
|
||||
static struct cas_classifier *_cas_cls_init(ocf_cache_t cache)
|
||||
{
|
||||
struct cas_classifier *cls;
|
||||
|
||||
cls = kzalloc(sizeof(*cls), GFP_KERNEL);
|
||||
if (!cls)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
INIT_LIST_HEAD(&cls->rules);
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
|
||||
cls->wq = alloc_workqueue("kcas_clsd", WQ_UNBOUND | WQ_FREEZABLE, 1);
|
||||
#else
|
||||
cls->wq = create_singlethread_workqueue("kcas_clsd");
|
||||
#endif
|
||||
if (!cls->wq) {
|
||||
kfree(cls);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
rwlock_init(&cls->lock);
|
||||
|
||||
CAS_CLS_MSG(KERN_INFO, "Initialized IO classifier\n");
|
||||
|
||||
return cls;
|
||||
}
|
||||
|
||||
/* Initialize classifier and create rules for existing I/O classes */
|
||||
int cas_cls_init(ocf_cache_t cache)
|
||||
{
|
||||
struct cas_classifier *cls;
|
||||
unsigned result = 0;
|
||||
unsigned i;
|
||||
|
||||
cls = _cas_cls_init(cache);
|
||||
if (IS_ERR(cls))
|
||||
return PTR_ERR(cls);
|
||||
cas_set_classifier(cache, cls);
|
||||
|
||||
/* Update rules for all I/O classes except 0 - this is default for all
|
||||
* unclassified I/O */
|
||||
for (i = 1; i < OCF_IO_CLASS_MAX; i++) {
|
||||
result = _cas_cls_rule_init(cache, i);
|
||||
if (result)
|
||||
break;
|
||||
}
|
||||
|
||||
if (result)
|
||||
cas_cls_deinit(cache);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Determine whether io matches rule */
|
||||
static cas_cls_eval_t cas_cls_process_rule(struct cas_classifier *cls,
|
||||
struct cas_cls_rule *r, struct cas_cls_io *io,
|
||||
ocf_part_id_t *part_id)
|
||||
{
|
||||
struct list_head *item;
|
||||
struct cas_cls_condition *c;
|
||||
cas_cls_eval_t ret = cas_cls_eval_no, rr;
|
||||
|
||||
CAS_CLS_DEBUG_TRACE(" Processing rule for class %d\n", r->part_id);
|
||||
list_for_each(item, &r->conditions) {
|
||||
|
||||
c = list_entry(item, struct cas_cls_condition, list);
|
||||
|
||||
if (!ret.yes && c->l_op == cas_cls_logical_and)
|
||||
break;
|
||||
|
||||
rr = c->handler->test(cls, c, io, *part_id);
|
||||
CAS_CLS_DEBUG_TRACE(" Processing condition %s => %d, stop:%d "
|
||||
"(l_op: %d)\n", c->handler->token, rr.yes,
|
||||
rr.stop, (int)c->l_op);
|
||||
|
||||
ret.yes = (c->l_op == cas_cls_logical_and) ?
|
||||
rr.yes && ret.yes :
|
||||
rr.yes || ret.yes;
|
||||
ret.stop = rr.stop;
|
||||
|
||||
if (ret.stop)
|
||||
break;
|
||||
}
|
||||
|
||||
CAS_CLS_DEBUG_TRACE(" Rule %d output => %d stop: %d\n", r->part_id,
|
||||
ret.yes, ret.stop);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Fill in cas_cls_io for given bio - it is assumed that ctx is
|
||||
* zeroed upon entry */
|
||||
static void _cas_cls_get_bio_context(struct bio *bio,
|
||||
struct cas_cls_io *ctx)
|
||||
{
|
||||
struct page *page = NULL;
|
||||
|
||||
if (!bio)
|
||||
return;
|
||||
ctx->bio = bio;
|
||||
|
||||
if (!SEGMENT_BVEC(bio_iovec(bio)))
|
||||
return;
|
||||
|
||||
page = bio_page(bio);
|
||||
|
||||
if (!page)
|
||||
return;
|
||||
ctx->page = page;
|
||||
|
||||
if (PageAnon(page))
|
||||
return;
|
||||
|
||||
if (PageSlab(page) || PageCompound(page))
|
||||
return;
|
||||
|
||||
if (!page->mapping)
|
||||
return;
|
||||
|
||||
ctx->inode = page->mapping->host;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Determine I/O class for bio */
|
||||
ocf_part_id_t cas_cls_classify(ocf_cache_t cache, struct bio *bio)
|
||||
{
|
||||
struct cas_classifier *cls;
|
||||
struct cas_cls_io io = {};
|
||||
struct list_head *item;
|
||||
struct cas_cls_rule *r;
|
||||
ocf_part_id_t part_id = 0;
|
||||
cas_cls_eval_t ret;
|
||||
|
||||
cls = cas_get_classifier(cache);
|
||||
ENV_BUG_ON(!cls);
|
||||
|
||||
_cas_cls_get_bio_context(bio, &io);
|
||||
|
||||
read_lock(&cls->lock);
|
||||
CAS_CLS_DEBUG_TRACE("%s\n", "Starting processing");
|
||||
list_for_each(item, &cls->rules) {
|
||||
r = list_entry(item, struct cas_cls_rule, list);
|
||||
ret = cas_cls_process_rule(cls, r, &io, &part_id);
|
||||
if (ret.yes)
|
||||
part_id = r->part_id;
|
||||
if (ret.stop)
|
||||
break;
|
||||
}
|
||||
read_unlock(&cls->lock);
|
||||
|
||||
return part_id;
|
||||
}
|
||||
|
33
modules/cas_cache/classifier.h
Normal file
33
modules/cas_cache/classifier.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright(c) 2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef __CLASSIFIER_H__
|
||||
#define __CLASSIFIER_H__
|
||||
|
||||
struct cas_cls_rule;
|
||||
|
||||
/* Initialize classifier and create rules for existing I/O classes */
|
||||
int cas_cls_init(ocf_cache_t cache);
|
||||
|
||||
/* Deinitialize classifier and remove rules */
|
||||
void cas_cls_deinit(ocf_cache_t cache);
|
||||
|
||||
/* Allocate and initialize classification rule */
|
||||
int cas_cls_rule_create(ocf_cache_t cache,
|
||||
ocf_part_id_t part_id, const char* rule,
|
||||
struct cas_cls_rule **cls_rule);
|
||||
|
||||
/* Deinit classification rule */
|
||||
void cas_cls_rule_destroy(ocf_cache_t cache, struct cas_cls_rule *r);
|
||||
|
||||
/* Bind classification rule to io class */
|
||||
void cas_cls_rule_apply(ocf_cache_t cache, ocf_part_id_t part_id,
|
||||
struct cas_cls_rule *r);
|
||||
|
||||
/* Determine I/O class for bio */
|
||||
ocf_part_id_t cas_cls_classify(ocf_cache_t cache, struct bio *bio);
|
||||
|
||||
|
||||
#endif
|
139
modules/cas_cache/classifier_defs.h
Normal file
139
modules/cas_cache/classifier_defs.h
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright(c) 2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef __CLASSIFIER_DEFS_H__
|
||||
#define __CLASSIFIER_DEFS_H__
|
||||
|
||||
/* Rule matches 1:1 with io class. It contains multiple conditions with
|
||||
* associated logical operator (and/or) */
|
||||
struct cas_cls_rule {
|
||||
/* Rules list element */
|
||||
struct list_head list;
|
||||
|
||||
/* Associated partition id */
|
||||
ocf_part_id_t part_id;
|
||||
|
||||
/* Conditions for this rule */
|
||||
struct list_head conditions;
|
||||
};
|
||||
|
||||
/* Classifier context - one per cache instance. */
|
||||
struct cas_classifier {
|
||||
/* Rules list head */
|
||||
struct list_head rules;
|
||||
|
||||
/* Directory inode resolving workqueue */
|
||||
struct workqueue_struct *wq;
|
||||
|
||||
/* Lock for rules list */
|
||||
rwlock_t lock;
|
||||
};
|
||||
|
||||
struct cas_cls_condition_handler;
|
||||
|
||||
/* cas_cls_condition represents single test (e.g. file_size <= 4K) plus
|
||||
* logical operator (and/or) to combine evaluation of this condition with
|
||||
* previous conditions within one rule */
|
||||
struct cas_cls_condition {
|
||||
/* Condition handler */
|
||||
struct cas_cls_condition_handler *handler;
|
||||
|
||||
/* Conditions list element */
|
||||
struct list_head list;
|
||||
|
||||
/* Data specific to this condition instance */
|
||||
void *context;
|
||||
|
||||
/* Logical operator to apply to previous conditions evaluation */
|
||||
int l_op;
|
||||
};
|
||||
|
||||
/* Helper structure aggregating I/O data often accessed by condition handlers */
|
||||
struct cas_cls_io {
|
||||
/* bio */
|
||||
struct bio *bio;
|
||||
|
||||
/* First page associated with bio */
|
||||
struct page *page;
|
||||
|
||||
/* Inode associated with page */
|
||||
struct inode *inode;
|
||||
};
|
||||
|
||||
/* Condition evaluation return flags */
|
||||
typedef struct cas_cls_eval {
|
||||
uint8_t yes : 1;
|
||||
uint8_t stop : 1;
|
||||
} cas_cls_eval_t;
|
||||
|
||||
static const cas_cls_eval_t cas_cls_eval_yes = { .yes = 1 };
|
||||
static const cas_cls_eval_t cas_cls_eval_no = { };
|
||||
|
||||
/* Logical operators */
|
||||
enum cas_cls_logical_op {
|
||||
cas_cls_logical_and = 0,
|
||||
cas_cls_logical_or
|
||||
};
|
||||
|
||||
/* Condition handler - abstraction over different kinds of condition checks
|
||||
* (e.g. file size, metadata). Does not contain all the data required to
|
||||
* evaluate condition (e.g. actual file size value), these are stored in
|
||||
* @context member of cas_cls_condition object, provided as input argument to
|
||||
* test, ctr and dtr callbacks. */
|
||||
struct cas_cls_condition_handler {
|
||||
/* String representing this condition class */
|
||||
const char *token;
|
||||
|
||||
/* Condition test */
|
||||
cas_cls_eval_t (*test)(struct cas_classifier *cls,
|
||||
struct cas_cls_condition *c, struct cas_cls_io *io,
|
||||
ocf_part_id_t part_id);
|
||||
|
||||
/* Condition constructor */
|
||||
int (*ctr)(struct cas_classifier *cls, struct cas_cls_condition *c,
|
||||
char *data);
|
||||
|
||||
/* Condition destructor */
|
||||
void (*dtr)(struct cas_classifier *cls, struct cas_cls_condition *c);
|
||||
};
|
||||
|
||||
/* Numeric condition numeric operators */
|
||||
enum cas_cls_numeric_op {
|
||||
cas_cls_numeric_eq = 0,
|
||||
cas_cls_numeric_ne = 1,
|
||||
cas_cls_numeric_lt = 2,
|
||||
cas_cls_numeric_gt = 3,
|
||||
cas_cls_numeric_le = 4,
|
||||
cas_cls_numeric_ge = 5,
|
||||
};
|
||||
|
||||
/* Numeric condition context */
|
||||
struct cas_cls_numeric {
|
||||
/* Arithmetic operator */
|
||||
enum cas_cls_numeric_op operator;
|
||||
|
||||
/* Condition operand as unsigned int */
|
||||
uint64_t v_u64;
|
||||
};
|
||||
|
||||
/* Directory condition context */
|
||||
struct cas_cls_directory {
|
||||
/* 1 if directory had been resolved */
|
||||
int resolved;
|
||||
|
||||
/* Dir path */
|
||||
char *pathname;
|
||||
|
||||
/* Resolved inode */
|
||||
unsigned long i_ino;
|
||||
|
||||
/* Back pointer to classifier context */
|
||||
struct cas_classifier *cls;
|
||||
|
||||
/* Work item associated with resolving dir for this condition */
|
||||
struct delayed_work d_work;
|
||||
};
|
||||
|
||||
#endif
|
482
modules/cas_cache/context.c
Normal file
482
modules/cas_cache/context.c
Normal file
@ -0,0 +1,482 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "cas_cache.h"
|
||||
#include "context.h"
|
||||
#include "utils/utils_rpool.h"
|
||||
#include "utils/utils_data.h"
|
||||
#include "utils/utils_gc.h"
|
||||
#include "threads.h"
|
||||
|
||||
struct ocf_mpool *cas_bvec_pool;
|
||||
|
||||
struct cas_reserve_pool *cas_bvec_pages_rpool;
|
||||
|
||||
#define CAS_ALLOC_PAGE_LIMIT 1024
|
||||
#define PG_cas PG_private
|
||||
|
||||
#define CAS_LOG_RATELIMIT HZ * 5
|
||||
/* High burst limit to ensure cache init logs are printed properly */
|
||||
#define CAS_LOG_BURST_LIMIT 50
|
||||
|
||||
static inline void _cas_page_set_priv(struct page *page)
|
||||
{
|
||||
set_bit(PG_cas , &page->flags);
|
||||
}
|
||||
|
||||
static inline void _cas_page_clear_priv(struct page *page)
|
||||
{
|
||||
clear_bit(PG_cas , &page->flags);
|
||||
page->private = 0;
|
||||
}
|
||||
|
||||
static inline int _cas_page_test_priv(struct page *page)
|
||||
{
|
||||
return test_bit(PG_cas , &page->flags);
|
||||
}
|
||||
|
||||
static void _cas_free_page_rpool(void *allocator_ctx, void *item)
|
||||
{
|
||||
struct page *page = virt_to_page(item);
|
||||
|
||||
_cas_page_clear_priv(page);
|
||||
__free_page(page);
|
||||
}
|
||||
|
||||
static void _cas_page_set_cpu(struct page *page, int cpu)
|
||||
{
|
||||
page->private = cpu;
|
||||
}
|
||||
|
||||
void *_cas_alloc_page_rpool(void *allocator_ctx, int cpu)
|
||||
{
|
||||
struct page *page;
|
||||
|
||||
page = alloc_page(GFP_NOIO | __GFP_NORETRY);
|
||||
if (!page)
|
||||
return NULL;
|
||||
|
||||
if (_cas_page_test_priv(page)) {
|
||||
printk(KERN_WARNING "CAS private bit is set\n");
|
||||
WARN(true, OCF_PREFIX_SHORT" CAS private bit is set\n");
|
||||
}
|
||||
|
||||
_cas_page_set_priv(page);
|
||||
_cas_page_set_cpu(page, cpu);
|
||||
return page_address(page);
|
||||
}
|
||||
|
||||
static int _cas_page_get_cpu(struct page *page)
|
||||
{
|
||||
return page->private;
|
||||
}
|
||||
|
||||
/* *** CONTEXT DATA OPERATIONS *** */
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
ctx_data_t *__cas_ctx_data_alloc(uint32_t pages, bool zalloc)
|
||||
{
|
||||
struct blk_data *data;
|
||||
uint32_t i;
|
||||
void *page_addr = NULL;
|
||||
struct page *page = NULL;
|
||||
int cpu;
|
||||
|
||||
data = ocf_mpool_new(cas_bvec_pool, pages);
|
||||
|
||||
if (!data) {
|
||||
CAS_PRINT_RL(KERN_ERR "Couldn't allocate BIO vector.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data->size = pages;
|
||||
|
||||
for (i = 0; i < pages; ++i) {
|
||||
page_addr = cas_rpool_try_get(cas_bvec_pages_rpool, &cpu);
|
||||
if (page_addr) {
|
||||
data->vec[i].bv_page = virt_to_page(page_addr);
|
||||
_cas_page_set_cpu(data->vec[i].bv_page, cpu);
|
||||
} else {
|
||||
data->vec[i].bv_page = alloc_page(GFP_NOIO);
|
||||
}
|
||||
|
||||
if (!data->vec[i].bv_page)
|
||||
break;
|
||||
|
||||
if (zalloc) {
|
||||
if (!page_addr) {
|
||||
page_addr = page_address(
|
||||
data->vec[i].bv_page);
|
||||
}
|
||||
memset(page_addr, 0, PAGE_SIZE);
|
||||
}
|
||||
|
||||
data->vec[i].bv_len = PAGE_SIZE;
|
||||
data->vec[i].bv_offset = 0;
|
||||
}
|
||||
|
||||
/* One of allocations failed */
|
||||
if (i != pages) {
|
||||
for (pages = 0; pages < i; pages++) {
|
||||
page = data->vec[i].bv_page;
|
||||
|
||||
if (page && !(_cas_page_test_priv(page) &&
|
||||
!cas_rpool_try_put(cas_bvec_pages_rpool,
|
||||
page_address(page),
|
||||
_cas_page_get_cpu(page)))) {
|
||||
__free_page(page);
|
||||
}
|
||||
}
|
||||
|
||||
ocf_mpool_del(cas_bvec_pool, data, pages);
|
||||
data = NULL;
|
||||
} else {
|
||||
/* Initialize iterator */
|
||||
cas_io_iter_init(&data->iter, data->vec, data->size);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
ctx_data_t *cas_ctx_data_alloc(uint32_t pages)
|
||||
{
|
||||
return __cas_ctx_data_alloc(pages, false);
|
||||
}
|
||||
|
||||
ctx_data_t *cas_ctx_data_zalloc(uint32_t pages)
|
||||
{
|
||||
return __cas_ctx_data_alloc(pages, true);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void cas_ctx_data_free(ctx_data_t *ctx_data)
|
||||
{
|
||||
uint32_t i;
|
||||
struct page *page = NULL;
|
||||
struct blk_data *data = ctx_data;
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
for (i = 0; i < data->size; i++) {
|
||||
page = data->vec[i].bv_page;
|
||||
|
||||
if (!(_cas_page_test_priv(page) && !cas_rpool_try_put(
|
||||
cas_bvec_pages_rpool,
|
||||
page_address(page),
|
||||
_cas_page_get_cpu(page))))
|
||||
__free_page(page);
|
||||
}
|
||||
|
||||
ocf_mpool_del(cas_bvec_pool, data, data->size);
|
||||
}
|
||||
|
||||
static int _cas_ctx_data_mlock(ctx_data_t *ctx_data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _cas_ctx_data_munlock(ctx_data_t *ctx_data)
|
||||
{
|
||||
}
|
||||
|
||||
void cas_ctx_data_secure_erase(ctx_data_t *ctx_data)
|
||||
{
|
||||
struct blk_data *data = ctx_data;
|
||||
uint32_t i;
|
||||
void *ptr;
|
||||
|
||||
for (i = 0; i < data->size; i++) {
|
||||
ptr = page_address(data->vec[i].bv_page);
|
||||
memset(ptr, 0, PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static uint32_t _cas_ctx_read_data(void *dst, ctx_data_t *src,
|
||||
uint32_t size)
|
||||
{
|
||||
struct blk_data *data = src;
|
||||
|
||||
return cas_io_iter_cpy_to_data(dst, &data->iter, size);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static uint32_t _cas_ctx_write_data(ctx_data_t *dst, const void *src,
|
||||
uint32_t size)
|
||||
{
|
||||
struct blk_data *data = dst;
|
||||
|
||||
return cas_io_iter_cpy_from_data(&data->iter, src, size);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static uint32_t _cas_ctx_zero_data(ctx_data_t *dst, uint32_t size)
|
||||
{
|
||||
struct blk_data *data = dst;
|
||||
|
||||
return cas_io_iter_zero(&data->iter, size);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static uint32_t _cas_ctx_seek_data(ctx_data_t *dst,
|
||||
ctx_data_seek_t seek, uint32_t offset)
|
||||
{
|
||||
struct blk_data *data = dst;
|
||||
|
||||
switch (seek) {
|
||||
case ctx_data_seek_begin:
|
||||
cas_io_iter_init(&data->iter, data->vec, data->size);
|
||||
|
||||
case ctx_data_seek_current:
|
||||
/* TODO Implement this if needed or remove this from enum */
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return cas_io_iter_move(&data->iter, offset);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static uint64_t _cas_ctx_data_copy(ctx_data_t *dst, ctx_data_t *src,
|
||||
uint64_t to, uint64_t from, uint64_t bytes)
|
||||
{
|
||||
struct blk_data *src_data = src, *dst_data = dst;
|
||||
|
||||
return cas_data_cpy(dst_data->vec, dst_data->size, src_data->vec,
|
||||
src_data->size, to, from, bytes);
|
||||
}
|
||||
|
||||
static int _cas_ctx_cleaner_init(ocf_cleaner_t c)
|
||||
{
|
||||
return cas_create_cleaner_thread(c);
|
||||
}
|
||||
|
||||
static void _cas_ctx_cleaner_stop(ocf_cleaner_t c)
|
||||
{
|
||||
return cas_stop_cleaner_thread(c);
|
||||
}
|
||||
|
||||
static int _cas_ctx_metadata_updater_init(ocf_metadata_updater_t mu)
|
||||
{
|
||||
return cas_create_metadata_updater_thread(mu);
|
||||
}
|
||||
|
||||
static void _cas_ctx_metadata_updater_kick(ocf_metadata_updater_t mu)
|
||||
{
|
||||
return cas_kick_metadata_updater_thread(mu);
|
||||
}
|
||||
|
||||
static void _cas_ctx_metadata_updater_stop(ocf_metadata_updater_t mu)
|
||||
{
|
||||
return cas_stop_metadata_updater_thread(mu);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static int _cas_ctx_logger_printf(ocf_logger_t logger, ocf_logger_lvl_t lvl,
|
||||
const char *fmt, va_list args)
|
||||
{
|
||||
static const char* level[] = {
|
||||
[log_emerg] = KERN_EMERG,
|
||||
[log_alert] = KERN_ALERT,
|
||||
[log_crit] = KERN_CRIT,
|
||||
[log_err] = KERN_ERR,
|
||||
[log_warn] = KERN_WARNING,
|
||||
[log_notice] = KERN_NOTICE,
|
||||
[log_info] = KERN_INFO,
|
||||
[log_debug] = KERN_DEBUG,
|
||||
};
|
||||
char *format;
|
||||
if (((unsigned)lvl) >= sizeof(level))
|
||||
return -EINVAL;
|
||||
|
||||
format = kasprintf(GFP_ATOMIC, "%s%s", level[lvl], fmt);
|
||||
if (!format)
|
||||
return -ENOMEM;
|
||||
|
||||
vprintk(format, args);
|
||||
|
||||
kfree(format);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static int _cas_ctx_logger_printf_rl(ocf_logger_t logger, const char *func_name)
|
||||
{
|
||||
static DEFINE_RATELIMIT_STATE(cas_log_rl, CAS_LOG_RATELIMIT,
|
||||
CAS_LOG_BURST_LIMIT);
|
||||
|
||||
if (!func_name)
|
||||
return -EINVAL;
|
||||
|
||||
return CAS_RATELIMIT(&cas_log_rl, func_name);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
static int _cas_ctx_logger_dump_stack(ocf_logger_t logger)
|
||||
{
|
||||
dump_stack();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ocf_ctx_config ctx_cfg = {
|
||||
.name = "CAS Linux Kernel",
|
||||
.ops = {
|
||||
.data = {
|
||||
.alloc = cas_ctx_data_alloc,
|
||||
.free = cas_ctx_data_free,
|
||||
.mlock = _cas_ctx_data_mlock,
|
||||
.munlock = _cas_ctx_data_munlock,
|
||||
.read = _cas_ctx_read_data,
|
||||
.write = _cas_ctx_write_data,
|
||||
.zero = _cas_ctx_zero_data,
|
||||
.seek = _cas_ctx_seek_data,
|
||||
.copy = _cas_ctx_data_copy,
|
||||
.secure_erase = cas_ctx_data_secure_erase,
|
||||
},
|
||||
|
||||
.cleaner = {
|
||||
.init = _cas_ctx_cleaner_init,
|
||||
.stop = _cas_ctx_cleaner_stop,
|
||||
},
|
||||
|
||||
.metadata_updater = {
|
||||
.init = _cas_ctx_metadata_updater_init,
|
||||
.kick = _cas_ctx_metadata_updater_kick,
|
||||
.stop = _cas_ctx_metadata_updater_stop,
|
||||
},
|
||||
|
||||
.logger = {
|
||||
.printf = _cas_ctx_logger_printf,
|
||||
.printf_rl = _cas_ctx_logger_printf_rl,
|
||||
.dump_stack = _cas_ctx_logger_dump_stack,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/* *** CONTEXT INITIALIZATION *** */
|
||||
|
||||
int cas_initialize_context(void)
|
||||
{
|
||||
struct blk_data data;
|
||||
int ret;
|
||||
|
||||
ret = ocf_ctx_init(&cas_ctx, &ctx_cfg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
cas_bvec_pool = ocf_mpool_create(NULL, sizeof(data),
|
||||
sizeof(data.vec[0]), GFP_NOIO, 7, "cas_biovec");
|
||||
|
||||
if (!cas_bvec_pool) {
|
||||
printk(KERN_ERR "Cannot create BIO vector memory pool\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_ctx;
|
||||
}
|
||||
|
||||
cas_bvec_pages_rpool = cas_rpool_create(CAS_ALLOC_PAGE_LIMIT,
|
||||
NULL, PAGE_SIZE, _cas_alloc_page_rpool,
|
||||
_cas_free_page_rpool, NULL);
|
||||
if (!cas_bvec_pages_rpool) {
|
||||
printk(KERN_ERR "Cannot create reserve pool for "
|
||||
"BIO vector memory pool\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_mpool;
|
||||
}
|
||||
|
||||
cas_garbage_collector_init();
|
||||
|
||||
ret = block_dev_init();
|
||||
if (ret) {
|
||||
printk(KERN_ERR "Cannot initialize block device layer\n");
|
||||
goto err_rpool;
|
||||
|
||||
}
|
||||
|
||||
ret = atomic_dev_init();
|
||||
if (ret) {
|
||||
printk(KERN_ERR "Cannot initialize atomic device layer\n");
|
||||
goto err_block_dev;
|
||||
}
|
||||
|
||||
ocf_mngt_core_pool_init(cas_ctx);
|
||||
|
||||
return 0;
|
||||
|
||||
err_block_dev:
|
||||
block_dev_deinit();
|
||||
err_rpool:
|
||||
cas_rpool_destroy(cas_bvec_pages_rpool, _cas_free_page_rpool, NULL);
|
||||
err_mpool:
|
||||
ocf_mpool_destroy(cas_bvec_pool);
|
||||
err_ctx:
|
||||
ocf_ctx_exit(cas_ctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cas_cleanup_context(void)
|
||||
{
|
||||
ocf_mngt_core_pool_deinit(cas_ctx);
|
||||
block_dev_deinit();
|
||||
atomic_dev_deinit();
|
||||
cas_garbage_collector_deinit();
|
||||
ocf_mpool_destroy(cas_bvec_pool);
|
||||
cas_rpool_destroy(cas_bvec_pages_rpool, _cas_free_page_rpool, NULL);
|
||||
|
||||
return ocf_ctx_exit(cas_ctx);
|
||||
}
|
||||
|
||||
/* *** CONTEXT DATA HELPER FUNCTION *** */
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
struct blk_data *cas_alloc_blk_data(uint32_t size, gfp_t flags)
|
||||
{
|
||||
struct blk_data *data = ocf_mpool_new_f(cas_bvec_pool, size, flags);
|
||||
|
||||
if (data)
|
||||
data->size = size;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
void cas_free_blk_data(struct blk_data *data)
|
||||
{
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
ocf_mpool_del(cas_bvec_pool, data, data->size);
|
||||
}
|
||||
|
79
modules/cas_cache/context.h
Normal file
79
modules/cas_cache/context.h
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __CONTEXT_H__
|
||||
#define __CONTEXT_H__
|
||||
|
||||
#include "linux_kernel_version.h"
|
||||
|
||||
struct bio_vec_iter {
|
||||
struct bio_vec *vec;
|
||||
uint32_t vec_size;
|
||||
uint32_t idx;
|
||||
uint32_t offset;
|
||||
uint32_t len;
|
||||
struct bio_vec *ivec;
|
||||
};
|
||||
|
||||
struct blk_data {
|
||||
/**
|
||||
* @brief Atomic counter for core device
|
||||
*/
|
||||
atomic_t master_remaining;
|
||||
|
||||
/**
|
||||
* @brief Core device request context (core private info)
|
||||
*/
|
||||
void *master_io_req;
|
||||
|
||||
/**
|
||||
* @brief CAS IO with which data is associated
|
||||
*/
|
||||
struct ocf_io *io;
|
||||
|
||||
/**
|
||||
* @brief List item used for IO splitting
|
||||
*/
|
||||
struct list_head list;
|
||||
|
||||
/**
|
||||
* @brief Timestamp of start processing request
|
||||
*/
|
||||
unsigned long long start_time;
|
||||
|
||||
/**
|
||||
* @brief Request data siz
|
||||
*/
|
||||
uint32_t size;
|
||||
|
||||
/**
|
||||
* @brief This filed indicates an error for request
|
||||
*/
|
||||
int error;
|
||||
|
||||
/**
|
||||
* @brief Iterator for accessing data
|
||||
*/
|
||||
struct bio_vec_iter iter;
|
||||
|
||||
/**
|
||||
* @brief Request data
|
||||
*/
|
||||
struct bio_vec vec[];
|
||||
};
|
||||
|
||||
struct blk_data *cas_alloc_blk_data(uint32_t size, gfp_t flags);
|
||||
void cas_free_blk_data(struct blk_data *data);
|
||||
|
||||
ctx_data_t *cas_ctx_data_alloc(uint32_t pages);
|
||||
ctx_data_t *cas_ctx_data_zalloc(uint32_t pages);
|
||||
void cas_ctx_data_free(ctx_data_t *ctx_data);
|
||||
void cas_ctx_data_secure_erase(ctx_data_t *ctx_data);
|
||||
|
||||
int cas_initialize_context(void);
|
||||
int cas_cleanup_context(void);
|
||||
|
||||
#endif /* __CONTEXT_H__ */
|
80
modules/cas_cache/control.c
Normal file
80
modules/cas_cache/control.c
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/fs.h>
|
||||
#include "linux_kernel_version.h"
|
||||
#include "service_ui_ioctl.h"
|
||||
#include "control.h"
|
||||
#include "cas_cache/cas_cache.h"
|
||||
|
||||
struct cas_ctrl_device {
|
||||
struct cdev cdev;
|
||||
struct class *class;
|
||||
dev_t dev;
|
||||
};
|
||||
|
||||
static struct cas_ctrl_device _control_device;
|
||||
|
||||
static const struct file_operations _ctrl_dev_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.unlocked_ioctl = cas_service_ioctl_ctrl
|
||||
};
|
||||
|
||||
int __init cas_ctrl_device_init(void)
|
||||
{
|
||||
struct cas_ctrl_device *ctrl = &_control_device;
|
||||
struct device *device;
|
||||
int result = 0;
|
||||
|
||||
result = alloc_chrdev_region(&ctrl->dev, 0, 1, "cas");
|
||||
if (result) {
|
||||
printk(KERN_ERR "Cannot allocate control chrdev number.\n");
|
||||
goto error_alloc_chrdev_region;
|
||||
}
|
||||
|
||||
cdev_init(&ctrl->cdev, &_ctrl_dev_fops);
|
||||
|
||||
result = cdev_add(&ctrl->cdev, ctrl->dev, 1);
|
||||
if (result) {
|
||||
printk(KERN_ERR "Cannot add control chrdev.\n");
|
||||
goto error_cdev_add;
|
||||
}
|
||||
|
||||
ctrl->class = class_create(THIS_MODULE, "cas");
|
||||
if (IS_ERR(ctrl->class)) {
|
||||
printk(KERN_ERR "Cannot create control chrdev class.\n");
|
||||
result = PTR_ERR(ctrl->class);
|
||||
goto error_class_create;
|
||||
}
|
||||
|
||||
device = device_create(ctrl->class, NULL, ctrl->dev, NULL,
|
||||
"cas_ctrl");
|
||||
if (IS_ERR(device)) {
|
||||
printk(KERN_ERR "Cannot create control chrdev.\n");
|
||||
result = PTR_ERR(device);
|
||||
goto error_device_create;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
error_device_create:
|
||||
class_destroy(ctrl->class);
|
||||
error_class_create:
|
||||
cdev_del(&ctrl->cdev);
|
||||
error_cdev_add:
|
||||
unregister_chrdev_region(ctrl->dev, 1);
|
||||
error_alloc_chrdev_region:
|
||||
return result;
|
||||
}
|
||||
|
||||
void __exit cas_ctrl_device_deinit(void)
|
||||
{
|
||||
struct cas_ctrl_device *ctrl = &_control_device;
|
||||
|
||||
device_destroy(ctrl->class, ctrl->dev);
|
||||
class_destroy(ctrl->class);
|
||||
cdev_del(&ctrl->cdev);
|
||||
unregister_chrdev_region(ctrl->dev, 1);
|
||||
}
|
11
modules/cas_cache/control.h
Normal file
11
modules/cas_cache/control.h
Normal file
@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
#ifndef __CAS_CONTROL_H__
|
||||
#define __CAS_CONTROL_H__
|
||||
|
||||
int __init cas_ctrl_device_init(void);
|
||||
void __exit cas_ctrl_device_deinit(void);
|
||||
|
||||
#endif
|
1863
modules/cas_cache/layer_cache_management.c
Normal file
1863
modules/cas_cache/layer_cache_management.c
Normal file
File diff suppressed because it is too large
Load Diff
1615
modules/cas_cache/layer_cache_management.c.orig
Normal file
1615
modules/cas_cache/layer_cache_management.c.orig
Normal file
File diff suppressed because it is too large
Load Diff
92
modules/cas_cache/layer_cache_management.h
Normal file
92
modules/cas_cache/layer_cache_management.h
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
#ifndef __LAYER_CACHE_MANAGEMENT_H__
|
||||
#define __LAYER_CACHE_MANAGEMENT_H__
|
||||
|
||||
#define CAS_BLK_DEV_REQ_TYPE_BIO 1
|
||||
#define CAS_BLK_DEV_REQ_TYPE_REQ 3
|
||||
|
||||
int cache_mng_set_cleaning_policy(ocf_cache_id_t cache_id, uint32_t type);
|
||||
|
||||
int cache_mng_get_cleaning_policy(ocf_cache_id_t cache_id, uint32_t *type);
|
||||
|
||||
int cache_mng_set_cleaning_param(ocf_cache_id_t cache_id, ocf_cleaning_t type,
|
||||
uint32_t param_id, uint32_t param_value);
|
||||
|
||||
int cache_mng_get_cleaning_param(ocf_cache_id_t cache_id, ocf_cleaning_t type,
|
||||
uint32_t param_id, uint32_t *param_value);
|
||||
|
||||
int cache_mng_add_core_to_cache(struct ocf_mngt_core_config *cfg,
|
||||
struct kcas_insert_core *cmd_info);
|
||||
|
||||
int cache_mng_remove_core_from_cache(struct kcas_remove_core *cmd);
|
||||
|
||||
int cache_mng_reset_core_stats(ocf_cache_id_t cache_id,
|
||||
ocf_core_id_t core_id);
|
||||
|
||||
int cache_mng_set_partitions(struct kcas_io_classes *cfg);
|
||||
|
||||
int cache_mng_exit_instance(ocf_cache_id_t id, int flush);
|
||||
|
||||
int cache_mng_prepare_cache_cfg(struct ocf_mngt_cache_config *cfg,
|
||||
struct ocf_mngt_cache_device_config *device_cfg,
|
||||
struct kcas_start_cache *cmd);
|
||||
|
||||
int cache_mng_core_pool_get_paths(struct kcas_core_pool_path *cmd_info);
|
||||
|
||||
int cache_mng_core_pool_remove(struct kcas_core_pool_remove *cmd_info);
|
||||
|
||||
int cache_mng_cache_check_device(struct kcas_cache_check_device *cmd_info);
|
||||
|
||||
int cache_mng_prepare_core_cfg(struct ocf_mngt_core_config *cfg,
|
||||
struct kcas_insert_core *cmd_info);
|
||||
|
||||
int cache_mng_init_instance(struct ocf_mngt_cache_config *cfg,
|
||||
struct ocf_mngt_cache_device_config *device_cfg,
|
||||
struct kcas_start_cache *cmd);
|
||||
|
||||
int cache_mng_set_seq_cutoff_threshold(ocf_cache_id_t id, ocf_core_id_t core_id,
|
||||
uint32_t thresh);
|
||||
|
||||
int cache_mng_set_seq_cutoff_policy(ocf_cache_id_t id, ocf_core_id_t core_id,
|
||||
ocf_seq_cutoff_policy policy);
|
||||
|
||||
int cache_mng_get_seq_cutoff_threshold(ocf_cache_id_t id, ocf_core_id_t core_id,
|
||||
uint32_t *thresh);
|
||||
|
||||
int cache_mng_get_seq_cutoff_policy(ocf_cache_id_t id, ocf_core_id_t core_id,
|
||||
ocf_seq_cutoff_policy *policy);
|
||||
|
||||
int cache_mng_set_cache_mode(ocf_cache_id_t id, ocf_cache_mode_t mode,
|
||||
uint8_t flush);
|
||||
|
||||
int cache_mng_flush_object(ocf_cache_id_t cache_id, ocf_core_id_t core_id);
|
||||
|
||||
int cache_mng_flush_device(ocf_cache_id_t id);
|
||||
|
||||
ocf_cache_line_t cache_mng_lookup(ocf_cache_t cache,
|
||||
ocf_core_id_t core_id, uint64_t core_cacheline);
|
||||
|
||||
int cache_mng_list_caches(struct kcas_cache_list *list);
|
||||
|
||||
int cache_mng_interrupt_flushing(ocf_cache_id_t id);
|
||||
|
||||
int cache_mng_get_info(struct kcas_cache_info *info);
|
||||
|
||||
int cache_mng_get_io_class_info(struct kcas_io_class *part);
|
||||
|
||||
int cache_mng_get_core_info(struct kcas_core_info *info);
|
||||
|
||||
void cache_mng_wait_for_rq_finish(ocf_cache_t cache);
|
||||
|
||||
int cache_mng_set_core_params(struct kcas_set_core_param *info);
|
||||
|
||||
int cache_mng_get_core_params(struct kcas_get_core_param *info);
|
||||
|
||||
int cache_mng_set_cache_params(struct kcas_set_cache_param *info);
|
||||
|
||||
int cache_mng_get_cache_params(struct kcas_get_cache_param *info);
|
||||
|
||||
#endif
|
1495
modules/cas_cache/layer_upgrade.c
Normal file
1495
modules/cas_cache/layer_upgrade.c
Normal file
File diff suppressed because it is too large
Load Diff
46
modules/cas_cache/layer_upgrade.h
Normal file
46
modules/cas_cache/layer_upgrade.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef __LAYER_UPGRADE_H
|
||||
|
||||
#define __LAYER_UPGRADE_H
|
||||
|
||||
#include "cas_cache/cas_cache.h"
|
||||
|
||||
extern bool in_upgrade;
|
||||
|
||||
/**
|
||||
* @brief Check that CAS is in upgarde state
|
||||
* @return true if is or false if isn't
|
||||
*/
|
||||
bool cas_upgrade_is_in_upgrade(void);
|
||||
|
||||
/**
|
||||
* @brief Check that caches configuration is stored at casdsk
|
||||
* @return 0 if exist
|
||||
*/
|
||||
int cas_upgrade_get_configuration(void);
|
||||
|
||||
/**
|
||||
* @brief Start upgrade in flight procedure, dump configuration,
|
||||
* switch caches to PT and close caches
|
||||
* @return result
|
||||
*/
|
||||
int cas_upgrade(void);
|
||||
|
||||
/**
|
||||
* @brief Finish upgrade in new CAS module - restore all caches
|
||||
* @return result of restoring
|
||||
*/
|
||||
int cas_upgrade_finish(void);
|
||||
|
||||
/**
|
||||
* @brief Try to parse configuration stored in casdisk
|
||||
* @return result of verification
|
||||
*/
|
||||
int cas_upgrade_verify(void);
|
||||
|
||||
#endif /* __LAYER_UPGRADE_H */
|
||||
|
624
modules/cas_cache/linux_kernel_version.h
Normal file
624
modules/cas_cache/linux_kernel_version.h
Normal file
@ -0,0 +1,624 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_KERNEL_VERSION_H__
|
||||
#define __LINUX_KERNEL_VERSION_H__
|
||||
|
||||
/* Libraries. */
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/bio.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/genhd.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sort.h>
|
||||
#include <linux/swap.h>
|
||||
#include <linux/thread_info.h>
|
||||
#include <asm-generic/ioctl.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/crc16.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/nmi.h>
|
||||
#include <linux/ratelimit.h>
|
||||
|
||||
#ifdef CONFIG_SLAB
|
||||
#include <linux/slab_def.h>
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 0, 0)
|
||||
#include <generated/utsrelease.h>
|
||||
#ifdef UTS_UBUNTU_RELEASE_ABI
|
||||
#define CAS_UBUNTU
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
|
||||
#error Unsupported Linux Kernel Version
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
|
||||
#define FILE_INODE(file) file->f_inode
|
||||
#else
|
||||
#define FILE_INODE(file) file->f_dentry->d_inode
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 76)
|
||||
#define DENTRY_ALIAS_HEAD(dentry) (dentry)->d_u.d_alias
|
||||
#define ALIAS_NODE_TO_DENTRY(alias) container_of(alias, struct dentry, d_u.d_alias)
|
||||
#else
|
||||
#define DENTRY_ALIAS_HEAD(dentry) (dentry)->d_alias
|
||||
#define ALIAS_NODE_TO_DENTRY(alias) container_of(alias, struct dentry, d_alias)
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
|
||||
#define ALIAS_NODE_TYPE struct hlist_node
|
||||
#define DENTRY_LIST_EMPTY(head) hlist_empty(head)
|
||||
#define INODE_FOR_EACH_DENTRY(pos, head) hlist_for_each(pos, head)
|
||||
#else
|
||||
#define DENTRY_LIST_EMPTY(head) list_empty(head)
|
||||
#define ALIAS_NODE_TYPE struct list_head
|
||||
#define INODE_FOR_EACH_DENTRY(pos, head) list_for_each(pos, head)
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||
#define BIO_OP_STATUS(bio) bio->bi_status
|
||||
#else
|
||||
#define BIO_OP_STATUS(bio) bio->bi_error
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
|
||||
#define BIO_ENDIO(BIO, BYTES_DONE, ERROR) \
|
||||
({ BIO_OP_STATUS(BIO) = ERROR; bio_endio(BIO); })
|
||||
#else
|
||||
#define BIO_ENDIO(BIO, BYTES_DONE, ERROR) bio_endio(BIO, ERROR)
|
||||
#endif
|
||||
|
||||
#define REFER_BLOCK_CALLBACK(name) name##_callback
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0)
|
||||
#define DECLARE_BLOCK_CALLBACK(name, BIO, BYTES_DONE, ERROR) \
|
||||
void name##_callback(BIO, ERROR)
|
||||
#define BLOCK_CALLBACK_INIT(BIO) {; }
|
||||
#define BLOCK_CALLBACK_RETURN() { return; }
|
||||
#define BLOCK_CALLBACK_ERROR(BIO, ERROR) ERROR
|
||||
#else
|
||||
#define DECLARE_BLOCK_CALLBACK(name, BIO, BYTES_DONE, ERROR) \
|
||||
void name##_callback(BIO)
|
||||
#define BLOCK_CALLBACK_INIT(BIO) {; }
|
||||
#define BLOCK_CALLBACK_RETURN() { return; }
|
||||
#define BLOCK_CALLBACK_ERROR(BIO, ERROR) BIO_OP_STATUS(BIO)
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 37)
|
||||
#define OPEN_BDEV_EXCLUSIVE(PATH, FMODE, HOLDER) \
|
||||
blkdev_get_by_path(PATH, (FMODE_EXCL | FMODE), HOLDER)
|
||||
#define CLOSE_BDEV_EXCLUSIVE(BDEV, FMODE) \
|
||||
blkdev_put(BDEV, (FMODE_EXCL | FMODE))
|
||||
#else
|
||||
#define OPEN_BDEV_EXCLUSIVE(PATH, FMODE, HOLDER) \
|
||||
open_bdev_exclusive(PATH, FMODE, HOLDER)
|
||||
#define CLOSE_BDEV_EXCLUSIVE(BDEV, FMODE) \
|
||||
close_bdev_exclusive(BDEV, FMODE)
|
||||
#endif
|
||||
|
||||
#ifdef CAS_UBUNTU
|
||||
#define LOOKUP_BDEV(PATH) lookup_bdev(PATH, 0)
|
||||
#else
|
||||
#define LOOKUP_BDEV(PATH) lookup_bdev(PATH)
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) || defined CAS_SLES12SP3
|
||||
#define BIO_OP_FLAGS_FORMAT "0x%016X"
|
||||
#define BIO_OP_FLAGS(bio) (bio)->bi_opf
|
||||
#else
|
||||
#define BIO_OP_FLAGS_FORMAT "0x%016lX"
|
||||
#define BIO_OP_FLAGS(bio) (bio)->bi_rw
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32)
|
||||
#define BIO_RW_FLAGS ((1U << BIO_RW_UNPLUG) | \
|
||||
(1U << BIO_RW_NOIDLE) | (1U << BIO_RW_SYNCIO))
|
||||
#define BIO_SET_RW_FLAGS(bio) BIO_OP_FLAGS((bio)) |= BIO_RW_FLAGS
|
||||
#else
|
||||
#define BIO_RW_FLAGS 0
|
||||
#define BIO_SET_RW_FLAGS(bio)
|
||||
#endif
|
||||
|
||||
#if defined RQF_SOFTBARRIER
|
||||
#define CHECK_BARRIER(bio) ((BIO_OP_FLAGS(bio) & RQF_SOFTBARRIER) != 0)
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 1)
|
||||
#define CHECK_BARRIER(bio) ((BIO_OP_FLAGS(bio) & REQ_SOFTBARRIER) != 0)
|
||||
#else
|
||||
#define CHECK_BARRIER(bio) (bio_rw_flagged((bio), BIO_RW_BARRIER))
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) || defined CAS_SLES12SP3
|
||||
#define RQ_DATA_DIR(rq) rq_data_dir(rq)
|
||||
#define RQ_DATA_DIR_WR WRITE
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
|
||||
#define RQ_DATA_DIR(rq) rq_data_dir(rq)
|
||||
#define RQ_DATA_DIR_WR REQ_WRITE
|
||||
#else
|
||||
#define RQ_DATA_DIR(rq) rq_data_dir(rq)
|
||||
#define RQ_DATA_DIR_WR WRITE
|
||||
#endif
|
||||
|
||||
#if defined REQ_PREFLUSH
|
||||
#define CAS_REQ_FLUSH REQ_PREFLUSH
|
||||
#define CAS_FLUSH_SUPPORTED
|
||||
#elif defined REQ_FLUSH
|
||||
#define CAS_REQ_FLUSH REQ_FLUSH
|
||||
#define CAS_FLUSH_SUPPORTED
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0) || defined CAS_SLES12SP3
|
||||
#define CHECK_QUEUE_FLUSH(q) test_bit(QUEUE_FLAG_WC, &(q)->queue_flags)
|
||||
#define CHECK_QUEUE_FUA(q) test_bit(QUEUE_FLAG_FUA, &(q)->queue_flags)
|
||||
|
||||
static inline void cas_set_queue_flush_fua(struct request_queue *q,
|
||||
bool flush, bool fua)
|
||||
{
|
||||
blk_queue_write_cache(q, flush, fua);
|
||||
}
|
||||
|
||||
#else
|
||||
#define CHECK_QUEUE_FLUSH(q) ((q)->flush_flags & CAS_REQ_FLUSH)
|
||||
#define CHECK_QUEUE_FUA(q) ((q)->flush_flags & REQ_FUA)
|
||||
|
||||
static inline void cas_set_queue_flush_fua(struct request_queue *q,
|
||||
bool flush, bool fua)
|
||||
{
|
||||
unsigned int flags = 0;
|
||||
if (flush)
|
||||
flags |= CAS_REQ_FLUSH;
|
||||
if (fua)
|
||||
flags |= REQ_FUA;
|
||||
if (flags)
|
||||
blk_queue_flush(q, flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
|
||||
|
||||
#ifdef WRITE_FLUSH
|
||||
#define RQ_IS_FLUSH(rq) ((rq)->cmd_flags & CAS_REQ_FLUSH)
|
||||
#ifdef BIO_FLUSH
|
||||
#define CAS_IS_WRITE_FLUSH(flags) ((flags) & BIO_FLUSH)
|
||||
#else
|
||||
#define CAS_IS_WRITE_FLUSH(flags) \
|
||||
((flags) & CAS_REQ_FLUSH)
|
||||
#endif
|
||||
|
||||
#define OCF_WRITE_FLUSH WRITE_FLUSH
|
||||
#elif defined REQ_PREFLUSH
|
||||
#define RQ_IS_FLUSH(rq) ((rq)->cmd_flags & REQ_PREFLUSH)
|
||||
#define OCF_WRITE_FLUSH (REQ_OP_WRITE | REQ_PREFLUSH)
|
||||
#define CAS_IS_WRITE_FLUSH(flags) \
|
||||
(OCF_WRITE_FLUSH == ((flags) & OCF_WRITE_FLUSH))
|
||||
#else
|
||||
#define RQ_IS_FLUSH(rq) 0
|
||||
#define CAS_IS_WRITE_FLUSH(flags) \
|
||||
(WRITE_BARRIER == ((flags) & WRITE_BARRIER))
|
||||
#define OCF_WRITE_FLUSH WRITE_BARRIER
|
||||
#endif /* #ifdef WRITE_FLUSH */
|
||||
|
||||
#ifdef WRITE_FLUSH_FUA
|
||||
#define OCF_WRITE_FLUSH_FUA WRITE_FLUSH_FUA
|
||||
#ifdef BIO_FUA
|
||||
#define CAS_IS_WRITE_FLUSH_FUA(flags) \
|
||||
((BIO_FUA | BIO_FLUSH) == \
|
||||
((flags) & (BIO_FUA | BIO_FLUSH)))
|
||||
#else
|
||||
#define CAS_IS_WRITE_FLUSH_FUA(flags) \
|
||||
((REQ_FUA | CAS_REQ_FLUSH) == \
|
||||
((flags) & (REQ_FUA | CAS_REQ_FLUSH)))
|
||||
#endif
|
||||
|
||||
#elif defined REQ_PREFLUSH
|
||||
#define CAS_IS_WRITE_FLUSH_FUA(flags) \
|
||||
((REQ_PREFLUSH | REQ_FUA) == \
|
||||
((flags) & (REQ_PREFLUSH |REQ_FUA)))
|
||||
#define OCF_WRITE_FLUSH_FUA (REQ_PREFLUSH | REQ_FUA)
|
||||
#else
|
||||
#define CAS_IS_WRITE_FLUSH_FUA(flags) 0
|
||||
#define OCF_WRITE_FLUSH_FUA WRITE_BARRIER
|
||||
#endif /* #ifdef WRITE_FLUSH_FUA */
|
||||
|
||||
#ifdef WRITE_FUA
|
||||
#ifdef BIO_FUA
|
||||
#define CAS_IS_WRITE_FUA(flags) ((flags) & BIO_FUA)
|
||||
#else
|
||||
#define CAS_IS_WRITE_FUA(flags) ((flags) & REQ_FUA)
|
||||
#endif
|
||||
#define OCF_WRITE_FUA WRITE_FUA
|
||||
#elif defined REQ_FUA
|
||||
#define CAS_IS_WRITE_FUA(flags) ((flags) & REQ_FUA)
|
||||
#define OCF_WRITE_FUA REQ_FUA
|
||||
#else
|
||||
#define CAS_IS_WRITE_FUA(flags) 0
|
||||
#define OCF_WRITE_FUA WRITE_BARRIER
|
||||
#endif /* #ifdef WRITE_FUA */
|
||||
|
||||
#endif /* #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) */
|
||||
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 7, 9)
|
||||
#define DAEMONIZE(name, arg...) daemonize(name, ##arg)
|
||||
#else
|
||||
#define DAEMONIZE(name, arg...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
|
||||
#define SET_QUEUE_CHUNK_SECTORS(queue, chunk_size) \
|
||||
queue->limits.chunk_sectors = chunk_size;
|
||||
#else
|
||||
#define SET_QUEUE_CHUNK_SECTORS(queue, chunk_size) {; }
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
|
||||
#define BIO_BISIZE(bio) bio->bi_size
|
||||
#define BIO_BIIDX(bio) bio->bi_idx
|
||||
#define BIO_BISECTOR(bio) bio->bi_sector
|
||||
#else
|
||||
#define BIO_BISIZE(bio) bio->bi_iter.bi_size
|
||||
#define BIO_BISECTOR(bio) bio->bi_iter.bi_sector
|
||||
#define BIO_BIIDX(bio) bio->bi_iter.bi_idx
|
||||
#endif
|
||||
|
||||
#ifdef CAS_SLES12SP3
|
||||
#define CAS_IS_DISCARD(bio) \
|
||||
(((BIO_OP_FLAGS(bio)) & REQ_OP_MASK) == REQ_OP_DISCARD)
|
||||
#define CAS_BIO_DISCARD \
|
||||
((REQ_OP_WRITE | REQ_OP_DISCARD))
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#define CAS_IS_DISCARD(bio) \
|
||||
(bio_op(bio) == REQ_OP_DISCARD)
|
||||
#define CAS_BIO_DISCARD \
|
||||
(REQ_OP_DISCARD)
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
|
||||
#define CAS_IS_DISCARD(bio) \
|
||||
((BIO_OP_FLAGS(bio)) & REQ_OP_DISCARD)
|
||||
#define CAS_BIO_DISCARD \
|
||||
((REQ_OP_WRITE | REQ_OP_DISCARD))
|
||||
#elif LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
|
||||
#define CAS_IS_DISCARD(bio) ((BIO_OP_FLAGS(bio)) & REQ_DISCARD)
|
||||
#define CAS_BIO_DISCARD (REQ_WRITE | REQ_DISCARD)
|
||||
#else
|
||||
#define CAS_IS_DISCARD(bio) ((BIO_OP_FLAGS(bio)) & (1 << BIO_RW_DISCARD))
|
||||
#define CAS_BIO_DISCARD ((1 << BIO_RW) | (1 << BIO_RW_DISCARD))
|
||||
#endif
|
||||
|
||||
#include <linux/mm.h>
|
||||
|
||||
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
|
||||
#include <uapi/asm-generic/mman-common.h>
|
||||
static inline unsigned long cas_vm_mmap(struct file *file,
|
||||
unsigned long addr, unsigned long len)
|
||||
{
|
||||
return vm_mmap(file, addr, len, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, 0);
|
||||
}
|
||||
|
||||
static inline int cas_vm_munmap(unsigned long start, size_t len)
|
||||
{
|
||||
return vm_munmap(start, len);
|
||||
}
|
||||
#else
|
||||
#include <asm-generic/mman-common.h>
|
||||
static inline unsigned long cas_vm_mmap(struct file *file,
|
||||
unsigned long addr, unsigned long len)
|
||||
{
|
||||
return do_mmap_pgoff(file, addr, len, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, 0);
|
||||
}
|
||||
|
||||
static inline int cas_vm_munmap(unsigned long start, size_t len)
|
||||
{
|
||||
return do_munmap(current->mm, start, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* For 8KB process kernel stack check if request is not continous and
|
||||
* submit each bio as separate request. This prevent nvme driver from
|
||||
* splitting requests.
|
||||
* For large requests, nvme splitting causes stack overrun.
|
||||
*/
|
||||
#if THREAD_SIZE <= 8192
|
||||
#define RQ_CHECK_CONTINOUS
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
|
||||
#define SEGMENT_BVEC(vec) (&(vec))
|
||||
#else
|
||||
#define SEGMENT_BVEC(vec) (vec)
|
||||
#endif
|
||||
|
||||
#ifndef SHRT_MIN
|
||||
#define SHRT_MIN ((s16)-32768)
|
||||
#endif
|
||||
|
||||
#ifndef SHRT_MAX
|
||||
#define SHRT_MAX ((s16)32767)
|
||||
#endif
|
||||
|
||||
#define ENOTSUP ENOTSUPP
|
||||
|
||||
#ifdef RHEL_RELEASE_VERSION
|
||||
#if RHEL_RELEASE_CODE == RHEL_RELEASE_VERSION(7, 3)
|
||||
#define CAS_RHEL_73
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) || defined CAS_SLES12SP3
|
||||
static inline blk_qc_t cas_submit_bio(int rw, struct bio *bio)
|
||||
{
|
||||
BIO_OP_FLAGS(bio) |= rw;
|
||||
return submit_bio(bio);
|
||||
}
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
|
||||
static inline blk_qc_t cas_submit_bio(int rw, struct bio *bio)
|
||||
{
|
||||
return submit_bio(rw, bio);
|
||||
}
|
||||
#else
|
||||
static inline void cas_submit_bio(int rw, struct bio *bio)
|
||||
{
|
||||
submit_bio(rw, bio);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||
#define cas_blk_rq_set_block_pc(rq) {}
|
||||
#else
|
||||
#define cas_blk_rq_set_block_pc(rq) blk_rq_set_block_pc(rq)
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||
#define cas_blk_queue_bounce(q, bounce_bio) ({})
|
||||
#else
|
||||
#define cas_blk_queue_bounce(q, bounce_bio) blk_queue_bounce(q, bounce_bio)
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 11)
|
||||
#define cas_blk_rq_append_bio(rq, bounce_bio) blk_rq_append_bio(rq, &bounce_bio)
|
||||
#else
|
||||
#define cas_blk_rq_append_bio(rq, bounce_bio) blk_rq_append_bio(rq, bounce_bio)
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) || defined CAS_SLES12SP3
|
||||
static inline struct request *cas_blk_make_request(struct request_queue *q,
|
||||
struct bio *bio, gfp_t gfp_mask)
|
||||
{
|
||||
struct request *rq = blk_get_request(q, bio_data_dir(bio), gfp_mask);
|
||||
|
||||
if (IS_ERR(rq))
|
||||
return rq;
|
||||
|
||||
cas_blk_rq_set_block_pc(rq);
|
||||
rq->q = q;
|
||||
|
||||
for_each_bio(bio) {
|
||||
struct bio *bounce_bio = bio;
|
||||
int ret;
|
||||
|
||||
cas_blk_queue_bounce(q, &bounce_bio);
|
||||
ret = cas_blk_rq_append_bio(rq, bounce_bio);
|
||||
if (unlikely(ret)) {
|
||||
blk_put_request(rq);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
}
|
||||
|
||||
return rq;
|
||||
}
|
||||
#else
|
||||
static inline struct request *cas_blk_make_request(struct request_queue *q,
|
||||
struct bio *bio, gfp_t gfp_mask)
|
||||
{
|
||||
return blk_make_request(q, bio, gfp_mask);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CAS_RHEL_73
|
||||
static inline void cas_copy_queue_limits(struct request_queue *exp_q,
|
||||
struct request_queue *cache_q, struct request_queue *core_q)
|
||||
{
|
||||
struct queue_limits_aux *l_aux = exp_q->limits.limits_aux;
|
||||
|
||||
exp_q->limits = cache_q->limits;
|
||||
exp_q->limits.limits_aux = l_aux;
|
||||
if (exp_q->limits.limits_aux && cache_q->limits.limits_aux)
|
||||
*exp_q->limits.limits_aux = *cache_q->limits.limits_aux;
|
||||
|
||||
exp_q->limits.max_sectors = core_q->limits.max_sectors;
|
||||
exp_q->limits.max_hw_sectors = core_q->limits.max_hw_sectors;
|
||||
exp_q->limits.max_segments = core_q->limits.max_segments;
|
||||
exp_q->limits.max_write_same_sectors = 0;
|
||||
|
||||
/*
|
||||
* Workaround for RHEL/CentOS 7.3 bug in kernel.
|
||||
* Merging implementation on blk-mq does not respec virt boundary
|
||||
* restriction and front merges bios with non-zero offsets.
|
||||
* This leads to request with gaps between bios and in consequence
|
||||
* triggers BUG_ON() in nvme driver or silently corrupts data.
|
||||
* To prevent this, disable merging on cache queue if there are
|
||||
* requirements regarding virt boundary (marking bios with REQ_NOMERGE
|
||||
* does not solve this problem).
|
||||
*/
|
||||
if (queue_virt_boundary(cache_q))
|
||||
queue_flag_set(QUEUE_FLAG_NOMERGES, cache_q);
|
||||
|
||||
}
|
||||
#else
|
||||
static inline void cas_copy_queue_limits(struct request_queue *exp_q,
|
||||
struct request_queue *cache_q, struct request_queue *core_q)
|
||||
{
|
||||
exp_q->limits = cache_q->limits;
|
||||
|
||||
exp_q->limits.max_sectors = core_q->limits.max_sectors;
|
||||
exp_q->limits.max_hw_sectors = core_q->limits.max_hw_sectors;
|
||||
exp_q->limits.max_segments = core_q->limits.max_segments;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) || defined CAS_SLES12SP3
|
||||
exp_q->limits.max_write_same_sectors = 0;
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) || defined CAS_SLES12SP3
|
||||
exp_q->limits.max_write_zeroes_sectors = 0;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)
|
||||
#define CAS_GARBAGE_COLLECTOR
|
||||
#endif
|
||||
|
||||
/* rate-limited printk */
|
||||
#define CAS_PRINT_RL(...) \
|
||||
if (printk_ratelimit()) \
|
||||
printk(__VA_ARGS__)
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
|
||||
static inline void cas_generic_start_io_acct(struct request_queue *q,
|
||||
int rw, unsigned long sectors, struct hd_struct *part)
|
||||
{
|
||||
int cpu = part_stat_lock();
|
||||
|
||||
part_round_stats(cpu, part);
|
||||
part_stat_inc(cpu, part, ios[rw]);
|
||||
part_stat_add(cpu, part, sectors[rw], sectors);
|
||||
part_inc_in_flight(part, rw);
|
||||
|
||||
part_stat_unlock();
|
||||
}
|
||||
|
||||
static inline void cas_generic_end_io_acct(struct request_queue *q,
|
||||
int rw, struct hd_struct *part, unsigned long start_time)
|
||||
{
|
||||
unsigned long duration = jiffies - start_time;
|
||||
int cpu = part_stat_lock();
|
||||
|
||||
part_stat_add(cpu, part, ticks[rw], duration);
|
||||
part_round_stats(cpu, part);
|
||||
part_dec_in_flight(part, rw);
|
||||
|
||||
part_stat_unlock();
|
||||
}
|
||||
#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
|
||||
static inline void cas_generic_start_io_acct(struct request_queue *q,
|
||||
int rw, unsigned long sectors, struct hd_struct *part)
|
||||
{
|
||||
generic_start_io_acct(rw, sectors, part);
|
||||
}
|
||||
|
||||
static inline void cas_generic_end_io_acct(struct request_queue *q,
|
||||
int rw, struct hd_struct *part, unsigned long start_time)
|
||||
{
|
||||
generic_end_io_acct(rw, part, start_time);
|
||||
}
|
||||
#else
|
||||
static inline void cas_generic_start_io_acct(struct request_queue *q,
|
||||
int rw, unsigned long sectors, struct hd_struct *part)
|
||||
{
|
||||
generic_start_io_acct(q, rw, sectors, part);
|
||||
}
|
||||
|
||||
static inline void cas_generic_end_io_acct(struct request_queue *q,
|
||||
int rw, struct hd_struct *part, unsigned long start_time)
|
||||
{
|
||||
generic_end_io_acct(q, rw, part, start_time);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
||||
static inline unsigned long cas_global_zone_page_state(enum zone_stat_item item)
|
||||
{
|
||||
return global_zone_page_state(item);
|
||||
}
|
||||
#define CAS_BIO_SET_DEV(bio, bdev) bio_set_dev(bio, bdev)
|
||||
#define CAS_BIO_GET_DEV(bio) bio->bi_disk
|
||||
#else
|
||||
static inline unsigned long cas_global_zone_page_state(enum zone_stat_item item)
|
||||
{
|
||||
return global_page_state(item);
|
||||
}
|
||||
#define CAS_BIO_SET_DEV(bio, bdev) bio->bi_bdev = bdev
|
||||
#define CAS_BIO_GET_DEV(bio) bio->bi_bdev->bd_disk
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)
|
||||
#define CAS_RATELIMIT(state, func_name) __ratelimit(state)
|
||||
#else
|
||||
#define CAS_RATELIMIT(state, func_name) ___ratelimit(state, func_name)
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
|
||||
static inline struct bio *cas_bio_clone(struct bio *bio, gfp_t gfp_mask)
|
||||
{
|
||||
return bio_clone_fast(bio, gfp_mask, NULL);
|
||||
}
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||
static inline struct bio *cas_bio_clone(struct bio *bio, gfp_t gfp_mask)
|
||||
{
|
||||
return bio_clone_kmalloc(bio, gfp_mask);
|
||||
}
|
||||
#define CAS_BLK_STATUS_T blk_status_t
|
||||
#else
|
||||
static inline struct bio *cas_bio_clone(struct bio *bio, gfp_t gfp_mask)
|
||||
{
|
||||
return bio_clone(bio, gfp_mask);
|
||||
}
|
||||
#define CAS_BLK_STATUS_T int
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||
static inline int is_rq_type_fs(struct request *rq)
|
||||
{
|
||||
switch (req_op(rq)){
|
||||
case REQ_OP_READ:
|
||||
case REQ_OP_WRITE:
|
||||
case REQ_OP_FLUSH:
|
||||
case REQ_OP_DISCARD:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#else
|
||||
static inline int is_rq_type_fs(struct request *rq)
|
||||
{
|
||||
return rq->cmd_type == REQ_TYPE_FS;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
|
||||
#define CAS_SET_DISCARD_ZEROES_DATA(queue_limits, val) ({})
|
||||
#else
|
||||
#define CAS_SET_DISCARD_ZEROES_DATA(queue_limits, val) \
|
||||
queue_limits.discard_zeroes_data = val
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
|
||||
#define cas_queue_flag_set_unlocked(flag, request_queue) \
|
||||
blk_queue_flag_set(flag, request_queue)
|
||||
#else
|
||||
#define cas_queue_flag_set_unlocked(flag, request_queue) \
|
||||
queue_flag_set_unlocked(flag, request_queue)
|
||||
#endif
|
||||
|
||||
#endif /* #ifndef __LINUX_KERNEL_VERSION_H__ */
|
210
modules/cas_cache/main.c
Normal file
210
modules/cas_cache/main.c
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "cas_cache.h"
|
||||
|
||||
/* Layer information. */
|
||||
MODULE_AUTHOR("Intel(R) Corporation");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_VERSION(CAS_VERSION);
|
||||
|
||||
u32 max_writeback_queue_size = 65536;
|
||||
module_param(max_writeback_queue_size, uint, (S_IRUSR | S_IRGRP));
|
||||
MODULE_PARM_DESC(max_writeback_queue_size,
|
||||
"Max cache writeback queue size (65536)");
|
||||
|
||||
u32 writeback_queue_unblock_size = 60000;
|
||||
module_param(writeback_queue_unblock_size, uint, (S_IRUSR | S_IRGRP));
|
||||
MODULE_PARM_DESC(writeback_queue_unblock_size,
|
||||
"Cache writeback queue size (60000) at which queue "
|
||||
"is unblocked when blocked");
|
||||
|
||||
u32 dry_run;
|
||||
module_param(dry_run, uint, (S_IRUSR | S_IRGRP));
|
||||
MODULE_PARM_DESC(dry_run, "Perform dry run on module load");
|
||||
|
||||
u32 use_io_scheduler = 1;
|
||||
module_param(use_io_scheduler, uint, (S_IRUSR | S_IRGRP));
|
||||
MODULE_PARM_DESC(use_io_scheduler,
|
||||
"Configure how IO shall be handled. "
|
||||
"0 - in make request function, 1 - in request function");
|
||||
|
||||
u32 metadata_layout = ocf_metadata_layout_default;
|
||||
module_param(metadata_layout, uint, (S_IRUSR | S_IRGRP));
|
||||
MODULE_PARM_DESC(metadata_layout, "Metadata layout, 0 - striping, 1 - sequential");
|
||||
|
||||
u32 unaligned_io = 1;
|
||||
module_param(unaligned_io, uint, (S_IRUSR | S_IRGRP));
|
||||
MODULE_PARM_DESC(unaligned_io,
|
||||
"Define how to handle I/O requests unaligned to 4 kiB, "
|
||||
"0 - apply PT, 1 - handle by cache");
|
||||
|
||||
u32 seq_cut_off_mb = 1;
|
||||
module_param(seq_cut_off_mb, uint, (S_IRUSR | S_IRGRP));
|
||||
MODULE_PARM_DESC(seq_cut_off_mb,
|
||||
"Sequential cut off threshold in MiB. 0 - disable");
|
||||
|
||||
/* globals */
|
||||
bool in_upgrade;
|
||||
ocf_ctx_t cas_ctx;
|
||||
struct casdsk_functions_mapper casdisk_functions;
|
||||
|
||||
struct exported_symbol {
|
||||
char *name;
|
||||
unsigned long addr;
|
||||
};
|
||||
|
||||
int static cas_find_symbol(void *data, const char *namebuf,
|
||||
struct module *module, unsigned long kallsyms_addresses)
|
||||
{
|
||||
struct exported_symbol *sym = data;
|
||||
|
||||
if (strcmp(namebuf, sym->name) == 0)
|
||||
sym->addr = kallsyms_addresses;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define cas_lookup_symbol(f) ({ \
|
||||
struct exported_symbol sym = {#f, 0}; \
|
||||
kallsyms_on_each_symbol(&cas_find_symbol, &sym); \
|
||||
casdisk_functions.f = (void *)sym.addr; \
|
||||
if (!casdisk_functions.f) \
|
||||
return -EINVAL; \
|
||||
})
|
||||
|
||||
int static cas_casdisk_lookup_funtions(void)
|
||||
{
|
||||
cas_lookup_symbol(casdsk_disk_dettach);
|
||||
cas_lookup_symbol(casdsk_exp_obj_destroy);
|
||||
cas_lookup_symbol(casdsk_exp_obj_create);
|
||||
cas_lookup_symbol(casdsk_disk_get_queue);
|
||||
cas_lookup_symbol(casdsk_store_config);
|
||||
cas_lookup_symbol(casdsk_disk_get_blkdev);
|
||||
cas_lookup_symbol(casdsk_exp_obj_get_queue);
|
||||
cas_lookup_symbol(casdsk_get_version);
|
||||
cas_lookup_symbol(casdsk_disk_close);
|
||||
cas_lookup_symbol(casdsk_disk_claim);
|
||||
cas_lookup_symbol(casdsk_exp_obj_unlock);
|
||||
cas_lookup_symbol(casdsk_disk_set_pt);
|
||||
cas_lookup_symbol(casdsk_get_stored_config);
|
||||
cas_lookup_symbol(casdsk_disk_get_gendisk);
|
||||
cas_lookup_symbol(casdsk_disk_attach);
|
||||
cas_lookup_symbol(casdsk_disk_set_attached);
|
||||
cas_lookup_symbol(casdsk_exp_obj_activate);
|
||||
cas_lookup_symbol(casdsk_exp_obj_activated);
|
||||
cas_lookup_symbol(casdsk_exp_obj_lock);
|
||||
cas_lookup_symbol(casdsk_free_stored_config);
|
||||
cas_lookup_symbol(casdsk_disk_open);
|
||||
cas_lookup_symbol(casdsk_disk_clear_pt);
|
||||
cas_lookup_symbol(casdsk_exp_obj_get_gendisk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init cas_init_module(void)
|
||||
{
|
||||
int result = 0;
|
||||
result = cas_casdisk_lookup_funtions();
|
||||
if (result) {
|
||||
printk(KERN_ERR OCF_PREFIX_SHORT
|
||||
"Could not find inteldisk functions.\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
if (casdisk_functions.casdsk_get_version() != CASDSK_IFACE_VERSION) {
|
||||
printk(KERN_ERR OCF_PREFIX_SHORT
|
||||
"Incompatible inteldisk module\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!writeback_queue_unblock_size || !max_writeback_queue_size) {
|
||||
printk(KERN_ERR OCF_PREFIX_SHORT
|
||||
"Invalid module parameter.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (writeback_queue_unblock_size >= max_writeback_queue_size) {
|
||||
printk(KERN_ERR OCF_PREFIX_SHORT
|
||||
"parameter writeback_queue_unblock_size"
|
||||
" must be less than max_writeback_queue_size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (metadata_layout >= ocf_metadata_layout_max) {
|
||||
printk(KERN_ERR OCF_PREFIX_SHORT
|
||||
"Invalid value for metadata_layout parameter\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (unaligned_io != 0 && unaligned_io != 1) {
|
||||
printk(KERN_ERR OCF_PREFIX_SHORT
|
||||
"Invalid value for unaligned_io parameter\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (use_io_scheduler != 0 && use_io_scheduler != 1) {
|
||||
printk(KERN_ERR OCF_PREFIX_SHORT
|
||||
"Invalid value for use_io_scheduler parameter\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
result = cas_initialize_context();
|
||||
if (result) {
|
||||
printk(KERN_ERR OCF_PREFIX_SHORT
|
||||
"Cannot initialize cache library\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
result = cas_upgrade_get_configuration();
|
||||
if (-KCAS_ERR_NO_STORED_CONF == result) {
|
||||
printk(KERN_INFO OCF_PREFIX_SHORT
|
||||
"Not found configuration for upgrade. "
|
||||
"Standard module initialization.\n");
|
||||
} else {
|
||||
if (!dry_run) {
|
||||
result = cas_upgrade_finish();
|
||||
if (result) {
|
||||
printk(KERN_ERR OCF_PREFIX_SHORT
|
||||
"Error during finish upgrade, "
|
||||
"result: %d\n", result);
|
||||
goto error_cas_ctx_init;
|
||||
}
|
||||
} else {
|
||||
result = cas_upgrade_verify();
|
||||
if (result) {
|
||||
printk(KERN_ERR OCF_PREFIX_SHORT
|
||||
"Error during upgrade "
|
||||
"verification\n");
|
||||
goto error_cas_ctx_init;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result = cas_ctrl_device_init();
|
||||
if (result) {
|
||||
printk(KERN_ERR OCF_PREFIX_SHORT
|
||||
"Cannot initialize control device\n");
|
||||
goto error_cas_ctx_init;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "%s Version %s (%s)::Module loaded successfully\n",
|
||||
OCF_PREFIX_LONG, CAS_VERSION, CAS_KERNEL);
|
||||
|
||||
return 0;
|
||||
|
||||
error_cas_ctx_init:
|
||||
cas_cleanup_context();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
module_init(cas_init_module);
|
||||
|
||||
static void __exit cas_exit_module(void)
|
||||
{
|
||||
cas_ctrl_device_deinit();
|
||||
cas_cleanup_context();
|
||||
}
|
||||
|
||||
module_exit(cas_exit_module);
|
284
modules/cas_cache/ocf_env.c
Normal file
284
modules/cas_cache/ocf_env.c
Normal file
@ -0,0 +1,284 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "cas_cache.h"
|
||||
#include "utils/utils_rpool.h"
|
||||
|
||||
/* *** ALLOCATOR *** */
|
||||
|
||||
#define CAS_ALLOC_ALLOCATOR_LIMIT 256
|
||||
|
||||
struct _env_allocator {
|
||||
/*!< Memory pool ID unique name */
|
||||
char *name;
|
||||
|
||||
/*!< Size of specific item of memory pool */
|
||||
uint32_t item_size;
|
||||
|
||||
/*!< OS handle to memory pool */
|
||||
struct kmem_cache *kmem_cache;
|
||||
|
||||
/*!< Number of currently allocated items in pool */
|
||||
atomic_t count;
|
||||
|
||||
struct cas_reserve_pool *rpool;
|
||||
};
|
||||
|
||||
static inline size_t env_allocator_align(size_t size)
|
||||
{
|
||||
if (size <= 2)
|
||||
return size;
|
||||
return (1ULL << 32) >> __builtin_clz(size - 1);
|
||||
}
|
||||
|
||||
struct _env_allocator_item {
|
||||
uint32_t flags;
|
||||
uint32_t cpu;
|
||||
char data[];
|
||||
};
|
||||
|
||||
void *env_allocator_new(env_allocator *allocator)
|
||||
{
|
||||
struct _env_allocator_item *item = NULL;
|
||||
int cpu;
|
||||
|
||||
item = cas_rpool_try_get(allocator->rpool, &cpu);
|
||||
if (item) {
|
||||
memset(item->data, 0, allocator->item_size -
|
||||
sizeof(struct _env_allocator_item));
|
||||
} else {
|
||||
item = kmem_cache_zalloc(allocator->kmem_cache, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
if (item) {
|
||||
item->cpu = cpu;
|
||||
atomic_inc(&allocator->count);
|
||||
return &item->data;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void *env_allocator_new_rpool(void *allocator_ctx, int cpu)
|
||||
{
|
||||
env_allocator *allocator = (env_allocator*) allocator_ctx;
|
||||
struct _env_allocator_item *item;
|
||||
|
||||
item = kmem_cache_zalloc(allocator->kmem_cache, GFP_NOIO |
|
||||
__GFP_NORETRY);
|
||||
|
||||
if (item) {
|
||||
item->flags = (GFP_NOIO | __GFP_NORETRY);
|
||||
item->cpu = cpu;
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
void env_allocator_del_rpool(void *allocator_ctx, void *item)
|
||||
{
|
||||
env_allocator *allocator = (env_allocator* ) allocator_ctx;
|
||||
|
||||
kmem_cache_free(allocator->kmem_cache, item);
|
||||
}
|
||||
|
||||
#define ENV_ALLOCATOR_NAME_MAX 128
|
||||
|
||||
env_allocator *env_allocator_create(uint32_t size, const char *name)
|
||||
{
|
||||
int error = -1;
|
||||
bool retry = true;
|
||||
|
||||
env_allocator *allocator = kzalloc(sizeof(*allocator), GFP_KERNEL);
|
||||
if (!allocator) {
|
||||
error = __LINE__;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (size < CAS_RPOOL_MIN_SIZE_ITEM) {
|
||||
printk(KERN_ERR "Can not create allocator."
|
||||
" Item size is too small.");
|
||||
ENV_WARN(true, OCF_PREFIX_SHORT" Can not create allocator."
|
||||
" Item size is too small.\n");
|
||||
error = __LINE__;
|
||||
goto err;
|
||||
}
|
||||
|
||||
allocator->item_size = size + sizeof(struct _env_allocator_item);
|
||||
if (allocator->item_size > PAGE_SIZE) {
|
||||
printk(KERN_WARNING "Creating allocator with item size"
|
||||
" greater than 4096B");
|
||||
ENV_WARN(true, OCF_PREFIX_SHORT" Creating allocator"
|
||||
" with item size greater than 4096B\n");
|
||||
}
|
||||
|
||||
allocator->name = kstrdup(name, ENV_MEM_NORMAL);
|
||||
|
||||
if (!allocator->name) {
|
||||
error = __LINE__;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Initialize kernel memory cache */
|
||||
#ifdef CONFIG_SLAB
|
||||
RETRY:
|
||||
#else
|
||||
(void)retry;
|
||||
#endif
|
||||
|
||||
allocator->kmem_cache = kmem_cache_create(allocator->name,
|
||||
allocator->item_size, 0, 0, NULL);
|
||||
if (!allocator->kmem_cache) {
|
||||
/* Can not setup kernel memory cache */
|
||||
error = __LINE__;
|
||||
goto err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SLAB
|
||||
if ((allocator->item_size < PAGE_SIZE)
|
||||
&& allocator->kmem_cache->gfporder) {
|
||||
/* Goal is to have one page allocation */
|
||||
if (retry) {
|
||||
retry = false;
|
||||
kmem_cache_destroy(allocator->kmem_cache);
|
||||
allocator->kmem_cache = NULL;
|
||||
allocator->item_size = env_allocator_align(allocator->item_size);
|
||||
goto RETRY;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Initialize reserve pool handler per cpu */
|
||||
|
||||
allocator->rpool = cas_rpool_create(CAS_ALLOC_ALLOCATOR_LIMIT,
|
||||
allocator->name, allocator->item_size, env_allocator_new_rpool,
|
||||
env_allocator_del_rpool, allocator);
|
||||
if (!allocator->rpool) {
|
||||
error = __LINE__;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return allocator;
|
||||
|
||||
err:
|
||||
printk(KERN_ERR "Cannot create memory allocator, ERROR %d", error);
|
||||
env_allocator_destroy(allocator);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void env_allocator_del(env_allocator *allocator, void *obj)
|
||||
{
|
||||
struct _env_allocator_item *item =
|
||||
container_of(obj, struct _env_allocator_item, data);
|
||||
|
||||
atomic_dec(&allocator->count);
|
||||
|
||||
if (item->flags == (GFP_NOIO | __GFP_NORETRY) &&
|
||||
!cas_rpool_try_put(allocator->rpool, item, item->cpu))
|
||||
return;
|
||||
|
||||
kmem_cache_free(allocator->kmem_cache, item);
|
||||
}
|
||||
|
||||
void env_allocator_destroy(env_allocator *allocator)
|
||||
{
|
||||
if (allocator) {
|
||||
cas_rpool_destroy(allocator->rpool, env_allocator_del_rpool,
|
||||
allocator);
|
||||
allocator->rpool = NULL;
|
||||
|
||||
if (atomic_read(&allocator->count)) {
|
||||
printk(KERN_CRIT "Not all object deallocated\n");
|
||||
ENV_WARN(true, OCF_PREFIX_SHORT" Cleanup problem\n");
|
||||
}
|
||||
|
||||
if (allocator->kmem_cache)
|
||||
kmem_cache_destroy(allocator->kmem_cache);
|
||||
|
||||
kfree(allocator->name);
|
||||
kfree(allocator);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t env_allocator_item_count(env_allocator *allocator)
|
||||
{
|
||||
return atomic_read(&allocator->count);
|
||||
}
|
||||
|
||||
static int env_sort_is_aligned(const void *base, int align)
|
||||
{
|
||||
return IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) ||
|
||||
((unsigned long)base & (align - 1)) == 0;
|
||||
}
|
||||
|
||||
static void env_sort_u32_swap(void *a, void *b, int size)
|
||||
{
|
||||
u32 t = *(u32 *)a;
|
||||
*(u32 *)a = *(u32 *)b;
|
||||
*(u32 *)b = t;
|
||||
}
|
||||
|
||||
static void env_sort_u64_swap(void *a, void *b, int size)
|
||||
{
|
||||
u64 t = *(u64 *)a;
|
||||
*(u64 *)a = *(u64 *)b;
|
||||
*(u64 *)b = t;
|
||||
}
|
||||
|
||||
static void env_sort_generic_swap(void *a, void *b, int size)
|
||||
{
|
||||
char t;
|
||||
|
||||
do {
|
||||
t = *(char *)a;
|
||||
*(char *)a++ = *(char *)b;
|
||||
*(char *)b++ = t;
|
||||
} while (--size > 0);
|
||||
}
|
||||
|
||||
void env_sort(void *base, size_t num, size_t size,
|
||||
int (*cmp_fn)(const void *, const void *),
|
||||
void (*swap_fn)(void *, void *, int size))
|
||||
{
|
||||
/* pre-scale counters for performance */
|
||||
int64_t i = (num/2 - 1) * size, n = num * size, c, r;
|
||||
|
||||
if (!swap_fn) {
|
||||
if (size == 4 && env_sort_is_aligned(base, 4))
|
||||
swap_fn = env_sort_u32_swap;
|
||||
else if (size == 8 && env_sort_is_aligned(base, 8))
|
||||
swap_fn = env_sort_u64_swap;
|
||||
else
|
||||
swap_fn = env_sort_generic_swap;
|
||||
}
|
||||
|
||||
/* heapify */
|
||||
for ( ; i >= 0; i -= size) {
|
||||
for (r = i; r * 2 + size < n; r = c) {
|
||||
c = r * 2 + size;
|
||||
if (c < n - size &&
|
||||
cmp_fn(base + c, base + c + size) < 0)
|
||||
c += size;
|
||||
if (cmp_fn(base + r, base + c) >= 0)
|
||||
break;
|
||||
swap_fn(base + r, base + c, size);
|
||||
}
|
||||
}
|
||||
|
||||
/* sort */
|
||||
for (i = n - size; i > 0; i -= size) {
|
||||
swap_fn(base, base + i, size);
|
||||
for (r = 0; r * 2 + size < i; r = c) {
|
||||
c = r * 2 + size;
|
||||
if (c < i - size &&
|
||||
cmp_fn(base + c, base + c + size) < 0)
|
||||
c += size;
|
||||
if (cmp_fn(base + r, base + c) >= 0)
|
||||
break;
|
||||
swap_fn(base + r, base + c, size);
|
||||
}
|
||||
}
|
||||
}
|
584
modules/cas_cache/ocf_env.h
Normal file
584
modules/cas_cache/ocf_env.h
Normal file
@ -0,0 +1,584 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __OCF_ENV_H__
|
||||
#define __OCF_ENV_H__
|
||||
|
||||
#include "linux_kernel_version.h"
|
||||
#include "utils/utils_gc.h"
|
||||
#include "ocf/ocf_err.h"
|
||||
|
||||
/* linux sector 512-bytes */
|
||||
#define ENV_SECTOR_SHIFT 9
|
||||
|
||||
/* *** MEMORY MANAGEMENT *** */
|
||||
|
||||
#define ENV_MEM_NORMAL GFP_KERNEL
|
||||
#define ENV_MEM_NOIO GFP_NOIO
|
||||
#define ENV_MEM_ATOMIC GFP_ATOMIC
|
||||
|
||||
static inline uint64_t env_get_free_memory(void)
|
||||
{
|
||||
return cas_global_zone_page_state(NR_FREE_PAGES) << PAGE_SHIFT;
|
||||
}
|
||||
|
||||
static inline void *env_malloc(size_t size, int flags)
|
||||
{
|
||||
return kmalloc(size, flags);
|
||||
}
|
||||
|
||||
static inline void *env_zalloc(size_t size, int flags)
|
||||
{
|
||||
return kzalloc(size, flags);
|
||||
}
|
||||
|
||||
static inline void env_free(const void *ptr)
|
||||
{
|
||||
kfree(ptr);
|
||||
}
|
||||
|
||||
static inline void *env_vmalloc(size_t size)
|
||||
{
|
||||
return vmalloc(size);
|
||||
}
|
||||
|
||||
static inline void *env_vzalloc(size_t size)
|
||||
{
|
||||
return vzalloc(size);
|
||||
}
|
||||
|
||||
static inline void env_vfree(const void *ptr)
|
||||
{
|
||||
cas_vfree(ptr);
|
||||
}
|
||||
|
||||
/* *** ALLOCATOR *** */
|
||||
|
||||
typedef struct _env_allocator env_allocator;
|
||||
|
||||
env_allocator *env_allocator_create(uint32_t size, const char *name);
|
||||
|
||||
void env_allocator_destroy(env_allocator *allocator);
|
||||
|
||||
void *env_allocator_new(env_allocator *allocator);
|
||||
|
||||
void env_allocator_del(env_allocator *allocator, void *item);
|
||||
|
||||
uint32_t env_allocator_item_count(env_allocator *allocator);
|
||||
|
||||
/* *** MUTEX *** */
|
||||
|
||||
typedef struct mutex env_mutex;
|
||||
|
||||
static inline int env_mutex_init(env_mutex *mutex)
|
||||
{
|
||||
mutex_init(mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void env_mutex_lock(env_mutex *mutex)
|
||||
{
|
||||
mutex_lock(mutex);
|
||||
}
|
||||
|
||||
static inline int env_mutex_lock_interruptible(env_mutex *mutex)
|
||||
{
|
||||
return mutex_lock_interruptible(mutex) ? -OCF_ERR_INTR : 0;
|
||||
}
|
||||
|
||||
static inline int env_mutex_trylock(env_mutex *mutex)
|
||||
{
|
||||
return mutex_trylock(mutex) ? 0 : -OCF_ERR_NO_LOCK;
|
||||
}
|
||||
|
||||
static inline void env_mutex_unlock(env_mutex *mutex)
|
||||
{
|
||||
mutex_unlock(mutex);
|
||||
}
|
||||
|
||||
static inline int env_mutex_is_locked(env_mutex *mutex)
|
||||
{
|
||||
return mutex_is_locked(mutex);
|
||||
}
|
||||
|
||||
/* *** RECURSIVE MUTEX *** */
|
||||
|
||||
typedef struct {
|
||||
struct mutex mutex;
|
||||
atomic_t count;
|
||||
struct task_struct *holder;
|
||||
} env_rmutex;
|
||||
|
||||
static inline int env_rmutex_init(env_rmutex *rmutex)
|
||||
{
|
||||
mutex_init(&rmutex->mutex);
|
||||
atomic_set(&rmutex->count, 0);
|
||||
rmutex->holder = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void env_rmutex_lock(env_rmutex *rmutex)
|
||||
{
|
||||
if (current == rmutex->holder) {
|
||||
atomic_inc(&rmutex->count);
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&rmutex->mutex);
|
||||
rmutex->holder = current;
|
||||
atomic_inc(&rmutex->count);
|
||||
}
|
||||
|
||||
static inline int env_rmutex_lock_interruptible(env_rmutex *rmutex)
|
||||
{
|
||||
int result = 0;
|
||||
if (current == rmutex->holder) {
|
||||
atomic_inc(&rmutex->count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
result = mutex_lock_interruptible(&rmutex->mutex);
|
||||
if (result) {
|
||||
/* No lock */
|
||||
return -OCF_ERR_INTR;
|
||||
}
|
||||
|
||||
rmutex->holder = current;
|
||||
atomic_inc(&rmutex->count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int env_rmutex_trylock(env_rmutex *rmutex)
|
||||
{
|
||||
if (current == rmutex->holder) {
|
||||
atomic_inc(&rmutex->count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mutex_trylock(&rmutex->mutex)) {
|
||||
/* No lock */
|
||||
return -OCF_ERR_NO_LOCK;
|
||||
}
|
||||
|
||||
rmutex->holder = current;
|
||||
atomic_inc(&rmutex->count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void env_rmutex_unlock(env_rmutex *rmutex)
|
||||
{
|
||||
BUG_ON(current != rmutex->holder);
|
||||
|
||||
if (atomic_dec_return(&rmutex->count)) {
|
||||
return;
|
||||
}
|
||||
|
||||
rmutex->holder = NULL;
|
||||
mutex_unlock(&rmutex->mutex);
|
||||
}
|
||||
|
||||
static inline int env_rmutex_is_locked(env_rmutex *rmutex)
|
||||
{
|
||||
return mutex_is_locked(&rmutex->mutex);
|
||||
}
|
||||
|
||||
/* *** RW SEMAPHORE *** */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct rw_semaphore sem;
|
||||
wait_queue_head_t wq;
|
||||
} env_rwsem;
|
||||
|
||||
static inline int env_rwsem_init(env_rwsem *s)
|
||||
{
|
||||
init_rwsem(&s->sem);
|
||||
init_waitqueue_head(&s->wq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void env_rwsem_up_read(env_rwsem *s)
|
||||
{
|
||||
up_read(&s->sem);
|
||||
wake_up_all(&s->wq);
|
||||
}
|
||||
|
||||
static inline void env_rwsem_down_read(env_rwsem *s)
|
||||
{
|
||||
down_read(&s->sem);
|
||||
}
|
||||
|
||||
static inline int env_rwsem_down_read_interruptible(env_rwsem *s)
|
||||
{
|
||||
return wait_event_interruptible(s->wq,
|
||||
down_read_trylock(&s->sem)) ? -OCF_ERR_INTR : 0;
|
||||
}
|
||||
|
||||
static inline int env_rwsem_down_read_trylock(env_rwsem *s)
|
||||
{
|
||||
return down_read_trylock(&s->sem) ? 0 : -OCF_ERR_NO_LOCK;
|
||||
}
|
||||
|
||||
static inline void env_rwsem_up_write(env_rwsem *s)
|
||||
{
|
||||
up_write(&s->sem);
|
||||
wake_up_all(&s->wq);
|
||||
}
|
||||
|
||||
static inline void env_rwsem_down_write(env_rwsem *s)
|
||||
{
|
||||
down_write(&s->sem);
|
||||
}
|
||||
|
||||
static inline int env_rwsem_down_write_interruptible(env_rwsem *s)
|
||||
{
|
||||
return wait_event_interruptible(s->wq,
|
||||
down_write_trylock(&s->sem)) ? -OCF_ERR_INTR : 0;
|
||||
}
|
||||
|
||||
static inline int env_rwsem_down_write_trylock(env_rwsem *s)
|
||||
{
|
||||
return down_write_trylock(&s->sem) ? 0 : -OCF_ERR_NO_LOCK;
|
||||
}
|
||||
|
||||
static inline int env_rwsem_is_locked(env_rwsem *s)
|
||||
{
|
||||
return rwsem_is_locked(&s->sem);
|
||||
}
|
||||
|
||||
/* *** COMPLETION *** */
|
||||
|
||||
typedef struct completion env_completion;
|
||||
|
||||
static inline void env_completion_init(env_completion *completion)
|
||||
{
|
||||
init_completion(completion);
|
||||
}
|
||||
|
||||
static inline void env_completion_wait(env_completion *completion)
|
||||
{
|
||||
wait_for_completion(completion);
|
||||
}
|
||||
|
||||
static inline void env_completion_complete(env_completion *completion)
|
||||
{
|
||||
complete(completion);
|
||||
}
|
||||
|
||||
/* *** ATOMIC VARIABLES *** */
|
||||
|
||||
typedef atomic_t env_atomic;
|
||||
typedef atomic64_t env_atomic64;
|
||||
|
||||
static inline int env_atomic_read(const env_atomic *a)
|
||||
{
|
||||
return atomic_read(a);
|
||||
}
|
||||
|
||||
static inline void env_atomic_set(env_atomic *a, int i)
|
||||
{
|
||||
atomic_set(a, i);
|
||||
}
|
||||
|
||||
static inline void env_atomic_add(int i, env_atomic *a)
|
||||
{
|
||||
atomic_add(i, a);
|
||||
}
|
||||
|
||||
static inline void env_atomic_sub(int i, env_atomic *a)
|
||||
{
|
||||
atomic_sub(i, a);
|
||||
}
|
||||
|
||||
static inline bool env_atomic_sub_and_test(int i, env_atomic *a)
|
||||
{
|
||||
return atomic_sub_and_test(i, a);
|
||||
}
|
||||
|
||||
static inline void env_atomic_inc(env_atomic *a)
|
||||
{
|
||||
atomic_inc(a);
|
||||
}
|
||||
|
||||
static inline void env_atomic_dec(env_atomic *a)
|
||||
{
|
||||
atomic_dec(a);
|
||||
}
|
||||
|
||||
static inline bool env_atomic_dec_and_test(env_atomic *a)
|
||||
{
|
||||
return atomic_dec_and_test(a);
|
||||
}
|
||||
|
||||
static inline bool env_atomic_inc_and_test(env_atomic *a)
|
||||
{
|
||||
return atomic_inc_and_test(a);
|
||||
}
|
||||
|
||||
static inline int env_atomic_add_return(int i, env_atomic *a)
|
||||
{
|
||||
return atomic_add_return(i, a);
|
||||
}
|
||||
|
||||
static inline int env_atomic_sub_return(int i, env_atomic *a)
|
||||
{
|
||||
return atomic_sub_return(i, a);
|
||||
}
|
||||
|
||||
static inline int env_atomic_inc_return(env_atomic *a)
|
||||
{
|
||||
return atomic_inc_return(a);
|
||||
}
|
||||
|
||||
static inline int env_atomic_dec_return(env_atomic *a)
|
||||
{
|
||||
return atomic_dec_return(a);
|
||||
}
|
||||
|
||||
static inline int env_atomic_cmpxchg(env_atomic *a, int old, int new_value)
|
||||
{
|
||||
return atomic_cmpxchg(a, old, new_value);
|
||||
}
|
||||
|
||||
static inline int env_atomic_add_unless(env_atomic *a, int i, int u)
|
||||
{
|
||||
return atomic_add_unless(a, i, u);
|
||||
}
|
||||
|
||||
static inline u64 env_atomic64_read(const env_atomic64 *a)
|
||||
{
|
||||
return atomic64_read(a);
|
||||
}
|
||||
|
||||
static inline void env_atomic64_set(env_atomic64 *a, u64 i)
|
||||
{
|
||||
atomic64_set(a, i);
|
||||
}
|
||||
|
||||
static inline void env_atomic64_add(u64 i, env_atomic64 *a)
|
||||
{
|
||||
atomic64_add(i, a);
|
||||
}
|
||||
|
||||
static inline void env_atomic64_sub(u64 i, env_atomic64 *a)
|
||||
{
|
||||
atomic64_sub(i, a);
|
||||
}
|
||||
|
||||
static inline void env_atomic64_inc(env_atomic64 *a)
|
||||
{
|
||||
atomic64_inc(a);
|
||||
}
|
||||
|
||||
static inline void env_atomic64_dec(env_atomic64 *a)
|
||||
{
|
||||
atomic64_dec(a);
|
||||
}
|
||||
|
||||
static inline u64 env_atomic64_inc_return(env_atomic64 *a)
|
||||
{
|
||||
return atomic64_inc_return(a);
|
||||
}
|
||||
|
||||
static inline u64 env_atomic64_cmpxchg(atomic64_t *a, u64 old, u64 new)
|
||||
{
|
||||
return atomic64_cmpxchg(a, old, new);
|
||||
}
|
||||
|
||||
/* *** SPIN LOCKS *** */
|
||||
|
||||
typedef spinlock_t env_spinlock;
|
||||
|
||||
static inline void env_spinlock_init(env_spinlock *l)
|
||||
{
|
||||
spin_lock_init(l);
|
||||
}
|
||||
|
||||
static inline void env_spinlock_lock(env_spinlock *l)
|
||||
{
|
||||
spin_lock(l);
|
||||
}
|
||||
|
||||
static inline void env_spinlock_unlock(env_spinlock *l)
|
||||
{
|
||||
spin_unlock(l);
|
||||
}
|
||||
|
||||
static inline void env_spinlock_lock_irq(env_spinlock *l)
|
||||
{
|
||||
spin_lock_irq(l);
|
||||
}
|
||||
|
||||
static inline void env_spinlock_unlock_irq(env_spinlock *l)
|
||||
{
|
||||
spin_unlock_irq(l);
|
||||
}
|
||||
|
||||
#define env_spinlock_lock_irqsave(l, flags) \
|
||||
spin_lock_irqsave((l), (flags))
|
||||
|
||||
#define env_spinlock_unlock_irqrestore(l, flags) \
|
||||
spin_unlock_irqrestore((l), (flags))
|
||||
|
||||
/* *** RW LOCKS *** */
|
||||
|
||||
typedef rwlock_t env_rwlock;
|
||||
|
||||
static inline void env_rwlock_init(env_rwlock *l)
|
||||
{
|
||||
rwlock_init(l);
|
||||
}
|
||||
|
||||
static inline void env_rwlock_read_lock(env_rwlock *l)
|
||||
{
|
||||
read_lock(l);
|
||||
}
|
||||
|
||||
static inline void env_rwlock_read_unlock(env_rwlock *l)
|
||||
{
|
||||
read_unlock(l);
|
||||
}
|
||||
|
||||
static inline void env_rwlock_write_lock(env_rwlock *l)
|
||||
{
|
||||
write_lock(l);
|
||||
}
|
||||
|
||||
static inline void env_rwlock_write_unlock(env_rwlock *l)
|
||||
{
|
||||
write_unlock(l);
|
||||
}
|
||||
|
||||
/* *** WAITQUEUE *** */
|
||||
|
||||
typedef wait_queue_head_t env_waitqueue;
|
||||
|
||||
static inline void env_waitqueue_init(env_waitqueue *w)
|
||||
{
|
||||
init_waitqueue_head(w);
|
||||
}
|
||||
|
||||
static inline void env_waitqueue_wake_up(env_waitqueue *w)
|
||||
{
|
||||
wake_up(w);
|
||||
}
|
||||
|
||||
#define env_waitqueue_wait(w, condition) \
|
||||
wait_event_interruptible((w), (condition))
|
||||
|
||||
/* *** SCHEDULING *** */
|
||||
static inline void env_cond_resched(void)
|
||||
{
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
static inline int env_in_interrupt(void)
|
||||
{
|
||||
return in_interrupt();;
|
||||
}
|
||||
|
||||
/* *** TIME *** */
|
||||
static inline uint64_t env_get_tick_count(void)
|
||||
{
|
||||
return jiffies;
|
||||
}
|
||||
|
||||
static inline uint64_t env_ticks_to_msecs(uint64_t j)
|
||||
{
|
||||
return jiffies_to_msecs(j);
|
||||
}
|
||||
|
||||
static inline uint64_t env_ticks_to_nsecs(uint64_t j)
|
||||
{
|
||||
return jiffies_to_usecs(j) * NSEC_PER_USEC;
|
||||
}
|
||||
|
||||
static inline bool env_time_after(uint64_t a, uint64_t b)
|
||||
{
|
||||
return time_after64(a,b);
|
||||
}
|
||||
|
||||
static inline uint64_t env_ticks_to_secs(uint64_t j)
|
||||
{
|
||||
return j >> SHIFT_HZ;
|
||||
}
|
||||
|
||||
static inline uint64_t env_secs_to_ticks(uint64_t j)
|
||||
{
|
||||
return j << SHIFT_HZ;
|
||||
}
|
||||
|
||||
/* *** BIT OPERATIONS *** */
|
||||
|
||||
static inline void env_bit_set(int nr, volatile void *addr)
|
||||
{
|
||||
set_bit(nr, addr);
|
||||
}
|
||||
|
||||
static inline void env_bit_clear(int nr, volatile void *addr)
|
||||
{
|
||||
clear_bit(nr, addr);
|
||||
}
|
||||
|
||||
static inline int env_bit_test(int nr, const void *addr)
|
||||
{
|
||||
return test_bit(nr, addr);
|
||||
}
|
||||
|
||||
static inline void env_msleep(uint64_t n)
|
||||
{
|
||||
msleep(n);
|
||||
}
|
||||
|
||||
/* *** STRING OPERATIONS *** */
|
||||
|
||||
|
||||
#define env_memset(dest, dmax, val) ({ \
|
||||
memset(dest, val, dmax); \
|
||||
0; \
|
||||
})
|
||||
#define env_memcpy(dest, dmax, src, slen) ({ \
|
||||
memcpy(dest, src, min_t(int, dmax, slen)); \
|
||||
0; \
|
||||
})
|
||||
#define env_memcmp(s1, s1max, s2, s2max, diff) ({ \
|
||||
*diff = memcmp(s1, s2, min_t(int, s1max, s2max)); \
|
||||
0; \
|
||||
})
|
||||
#define env_strdup kstrdup
|
||||
#define env_strnlen(s, smax) strnlen(s, smax)
|
||||
#define env_strncmp strncmp
|
||||
#define env_strncpy(dest, dmax, src, slen) ({ \
|
||||
strlcpy(dest, src, min_t(int, dmax, slen)); \
|
||||
0; \
|
||||
})
|
||||
|
||||
/* *** SORTING *** */
|
||||
|
||||
void env_sort(void *base, size_t num, size_t size,
|
||||
int (*cmp_fn)(const void *, const void *),
|
||||
void (*swap_fn)(void *, void *, int size));
|
||||
|
||||
/* *** CRC *** */
|
||||
|
||||
static inline uint32_t env_crc32(uint32_t crc, uint8_t const *data, size_t len)
|
||||
{
|
||||
return crc32(crc, data, len);
|
||||
}
|
||||
|
||||
/* *** LOGGING *** */
|
||||
|
||||
#define ENV_PRIu64 "llu"
|
||||
|
||||
#define ENV_WARN(cond, fmt...) WARN(cond, fmt)
|
||||
#define ENV_WARN_ON(cond) WARN_ON(cond)
|
||||
|
||||
#define ENV_BUG() BUG()
|
||||
#define ENV_BUG_ON(cond) BUG_ON(cond)
|
||||
|
||||
#endif /* __OCF_ENV_H__ */
|
21
modules/cas_cache/ocf_env_headers.h
Normal file
21
modules/cas_cache/ocf_env_headers.h
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __OCF_ENV_HEADERS_H__
|
||||
#define __OCF_ENV_HEADERS_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* TODO: Move prefix printing to context logger. */
|
||||
#define OCF_LOGO "Open-CAS"
|
||||
#define OCF_PREFIX_SHORT "[" OCF_LOGO "] "
|
||||
#define OCF_PREFIX_LONG "Open Cache Acceleration Software Linux"
|
||||
|
||||
#define OCF_VERSION_MAIN CAS_VERSION_MAIN
|
||||
#define OCF_VERSION_MAJOR CAS_VERSION_MAJOR
|
||||
#define OCF_VERSION_MINOR CAS_VERSION_MINOR
|
||||
|
||||
#endif /* __OCF_ENV_HEADERS_H__ */
|
414
modules/cas_cache/service_ui_ioctl.c
Normal file
414
modules/cas_cache/service_ui_ioctl.c
Normal file
@ -0,0 +1,414 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "cas_cache.h"
|
||||
|
||||
struct {
|
||||
int cas_error;
|
||||
int std_error;
|
||||
} static cas_error_code_map[] = {
|
||||
/* IOC error mappings*/
|
||||
{ OCF_ERR_INVAL, EINVAL },
|
||||
{ OCF_ERR_INVAL_VOLUME_TYPE, EINVAL },
|
||||
{ OCF_ERR_INTR, EINTR },
|
||||
{ OCF_ERR_UNKNOWN, EINVAL },
|
||||
{ OCF_ERR_TOO_MANY_CACHES, ENOSPC },
|
||||
{ OCF_ERR_NO_MEM, ENOMEM },
|
||||
{ OCF_ERR_NO_FREE_RAM, ENOMEM },
|
||||
{ OCF_ERR_START_CACHE_FAIL, EFAULT },
|
||||
{ OCF_ERR_CACHE_IN_USE, EBUSY },
|
||||
{ OCF_ERR_CACHE_NOT_EXIST, ENODEV },
|
||||
{ OCF_ERR_CACHE_EXIST, EEXIST },
|
||||
{ OCF_ERR_TOO_MANY_CORES, ENOSPC },
|
||||
{ OCF_ERR_CORE_NOT_AVAIL, ENAVAIL },
|
||||
{ OCF_ERR_NOT_OPEN_EXC, EBUSY },
|
||||
{ OCF_ERR_CACHE_NOT_AVAIL, ENAVAIL },
|
||||
{ OCF_ERR_IO_CLASS_NOT_EXIST, ENODEV },
|
||||
{ OCF_ERR_WRITE_CACHE, EIO },
|
||||
{ OCF_ERR_WRITE_CORE, EIO },
|
||||
{ OCF_ERR_DIRTY_SHUTDOWN, EFAULT },
|
||||
{ OCF_ERR_DIRTY_EXISTS, EFAULT },
|
||||
{ OCF_ERR_FLUSHING_INTERRUPTED, EINTR },
|
||||
|
||||
/* CAS kernel error mappings*/
|
||||
{ KCAS_ERR_ROOT, EPERM },
|
||||
{ KCAS_ERR_SYSTEM, EINVAL },
|
||||
{ KCAS_ERR_BAD_RANGE, ERANGE },
|
||||
{ KCAS_ERR_DEV_SPACE, ENOSPC },
|
||||
{ KCAS_ERR_INV_IOCTL, EINVAL },
|
||||
{ KCAS_ERR_DEV_PENDING, EBUSY },
|
||||
{ KCAS_ERR_DIRTY_EXISTS_NVME, EFAULT },
|
||||
{ KCAS_ERR_FILE_EXISTS, EEXIST },
|
||||
{ KCAS_ERR_IN_UPGRADE, EFAULT },
|
||||
{ KCAS_ERR_UNALIGNED, EINVAL },
|
||||
{ KCAS_ERR_NO_STORED_CONF, EINTR },
|
||||
{ KCAS_ERR_ROLLBACK, EFAULT },
|
||||
{ KCAS_ERR_NOT_NVME, ENODEV },
|
||||
{ KCAS_ERR_FORMAT_FAILED, EFAULT },
|
||||
{ KCAS_ERR_NVME_BAD_FORMAT, EINVAL },
|
||||
{ KCAS_ERR_CONTAINS_PART, EINVAL },
|
||||
{ KCAS_ERR_A_PART, EINVAL },
|
||||
{ KCAS_ERR_REMOVED_DIRTY, EIO },
|
||||
{ KCAS_ERR_STOPPED_DIRTY, EIO },
|
||||
};
|
||||
|
||||
/*******************************************/
|
||||
/* Helper which change cas-specific error */
|
||||
/* codes to kernel generic error codes */
|
||||
/*******************************************/
|
||||
|
||||
int map_cas_err_to_generic_code(int cas_error_code)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (cas_error_code == 0)
|
||||
return 0; /* No Error */
|
||||
|
||||
cas_error_code = abs(cas_error_code);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cas_error_code_map); i++) {
|
||||
if (cas_error_code_map[i].cas_error == cas_error_code)
|
||||
return -cas_error_code_map[i].std_error;
|
||||
}
|
||||
|
||||
return -cas_error_code;
|
||||
}
|
||||
|
||||
#define _GET_CMD_INFO(cmd_info, arg, size) ({ \
|
||||
cmd_info = vmalloc(size); \
|
||||
if (!cmd_info) \
|
||||
return -ENOMEM; \
|
||||
if (copy_from_user(cmd_info, (void __user *)arg, size)) { \
|
||||
printk(KERN_ALERT "Cannot copy cmd info from user space\n"); \
|
||||
vfree(cmd_info); \
|
||||
return -EINVAL; \
|
||||
} \
|
||||
})
|
||||
|
||||
#define GET_CMD_INFO(cmd_info, arg) _GET_CMD_INFO(cmd_info, arg, \
|
||||
sizeof(*cmd_info))
|
||||
|
||||
#define RETURN_CMD_RESULT(cmd_info, arg, result) ({ \
|
||||
int ret = result; \
|
||||
cmd_info->ext_err_code = abs(result); \
|
||||
if (copy_to_user((void __user *)arg, cmd_info, sizeof(*cmd_info))) { \
|
||||
printk(KERN_ALERT "Unable to copy response to user\n"); \
|
||||
ret = -EFAULT; \
|
||||
} \
|
||||
vfree(cmd_info); \
|
||||
return map_cas_err_to_generic_code(ret); \
|
||||
})
|
||||
|
||||
/* this handles IOctl for /dev/cas */
|
||||
/*********************************************/
|
||||
long cas_service_ioctl_ctrl(struct file *filp, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
if (_IOC_TYPE(cmd) != KCAS_IOCTL_MAGIC)
|
||||
return -EINVAL;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN)) {
|
||||
/* Must be root to issue ioctls */
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (cas_upgrade_is_in_upgrade() &&
|
||||
cmd != KCAS_IOCTL_CACHE_INFO &&
|
||||
cmd != KCAS_IOCTL_LIST_CACHE &&
|
||||
cmd != KCAS_IOCTL_GET_CACHE_COUNT &&
|
||||
cmd != KCAS_IOCTL_CORE_INFO &&
|
||||
cmd != KCAS_IOCTL_PARTITION_STATS &&
|
||||
cmd != KCAS_IOCTL_GET_CAPABILITIES) {
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case KCAS_IOCTL_START_CACHE: {
|
||||
struct kcas_start_cache *cmd_info;
|
||||
struct ocf_mngt_cache_config cfg;
|
||||
struct ocf_mngt_cache_device_config device_cfg;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
retval = cache_mng_prepare_cache_cfg(&cfg, &device_cfg, cmd_info);
|
||||
if (retval)
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
|
||||
retval = cache_mng_init_instance(&cfg, &device_cfg, cmd_info);
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
}
|
||||
|
||||
case KCAS_IOCTL_STOP_CACHE: {
|
||||
struct kcas_stop_cache *cmd_info;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
retval = cache_mng_exit_instance(cmd_info->cache_id,
|
||||
cmd_info->flush_data);
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
}
|
||||
|
||||
case KCAS_IOCTL_SET_CACHE_STATE: {
|
||||
struct kcas_set_cache_state *cmd_info;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
retval = cache_mng_set_cache_mode(cmd_info->cache_id,
|
||||
cmd_info->caching_mode, cmd_info->flush_data);
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
}
|
||||
|
||||
case KCAS_IOCTL_INSERT_CORE: {
|
||||
struct kcas_insert_core *cmd_info;
|
||||
struct ocf_mngt_core_config cfg;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
retval = cache_mng_prepare_core_cfg(&cfg, cmd_info);
|
||||
if (retval)
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
|
||||
retval = cache_mng_add_core_to_cache(&cfg, cmd_info);
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
}
|
||||
|
||||
case KCAS_IOCTL_REMOVE_CORE: {
|
||||
struct kcas_remove_core *cmd_info;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
retval = cache_mng_remove_core_from_cache(cmd_info);
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
}
|
||||
|
||||
case KCAS_IOCTL_RESET_STATS: {
|
||||
struct kcas_reset_stats *cmd_info;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
retval = cache_mng_reset_core_stats(cmd_info->cache_id,
|
||||
cmd_info->core_id);
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
}
|
||||
|
||||
case KCAS_IOCTL_FLUSH_CACHE: {
|
||||
struct kcas_flush_cache *cmd_info;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
retval = cache_mng_flush_device(cmd_info->cache_id);
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
}
|
||||
|
||||
case KCAS_IOCTL_INTERRUPT_FLUSHING: {
|
||||
struct kcas_interrupt_flushing *cmd_info;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
retval = cache_mng_interrupt_flushing(cmd_info->cache_id);
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
}
|
||||
|
||||
case KCAS_IOCTL_FLUSH_CORE: {
|
||||
struct kcas_flush_core *cmd_info;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
retval = cache_mng_flush_object(cmd_info->cache_id,
|
||||
cmd_info->core_id);
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
}
|
||||
|
||||
case KCAS_IOCTL_CACHE_INFO: {
|
||||
struct kcas_cache_info *cmd_info;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
retval = cache_mng_get_info(cmd_info);
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
}
|
||||
|
||||
case KCAS_IOCTL_CORE_INFO: {
|
||||
struct kcas_core_info *cmd_info;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
retval = cache_mng_get_core_info(cmd_info);
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
}
|
||||
|
||||
case KCAS_IOCTL_PARTITION_STATS: {
|
||||
struct kcas_io_class *cmd_info;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
retval = cache_mng_get_io_class_info(cmd_info);
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
|
||||
}
|
||||
|
||||
case KCAS_IOCTL_PARTITION_SET: {
|
||||
struct kcas_io_classes *cmd_info;
|
||||
|
||||
/* copy entire memory from user, including array of
|
||||
* ocf_io_class_info structs past the end of kcas_io_classes */
|
||||
_GET_CMD_INFO(cmd_info, arg, KCAS_IO_CLASSES_SIZE);
|
||||
|
||||
retval = cache_mng_set_partitions(cmd_info);
|
||||
|
||||
/* return just sizeof(struct kcas_io_classes) bytes of data */
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
}
|
||||
|
||||
case KCAS_IOCTL_GET_CACHE_COUNT: {
|
||||
struct kcas_cache_count *cmd_info;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
cmd_info->cache_count = ocf_mngt_cache_get_count(cas_ctx);
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, 0);
|
||||
}
|
||||
|
||||
case KCAS_IOCTL_LIST_CACHE: {
|
||||
struct kcas_cache_list *cmd_info;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
retval = cache_mng_list_caches(cmd_info);
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval > 0 ? 0 : retval);
|
||||
}
|
||||
|
||||
case KCAS_IOCTL_GET_CAPABILITIES: {
|
||||
struct kcas_capabilites *cmd_info;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
memset(cmd_info, 0, sizeof(*cmd_info));
|
||||
#ifdef CAS_NVME_FULL
|
||||
cmd_info->nvme_format = 1;
|
||||
#endif
|
||||
RETURN_CMD_RESULT(cmd_info, arg, 0);
|
||||
}
|
||||
|
||||
case KCAS_IOCTL_UPGRADE: {
|
||||
struct kcas_upgrade *cmd_info;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
retval = cas_upgrade();
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
}
|
||||
|
||||
#if defined(CAS_NVME_FULL)
|
||||
case KCAS_IOCTL_NVME_FORMAT: {
|
||||
struct kcas_nvme_format *cmd_info;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
retval = cas_nvme_format_optimal(
|
||||
cmd_info->device_path_name,
|
||||
cmd_info->metadata_mode,
|
||||
cmd_info->force);
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
}
|
||||
#endif
|
||||
|
||||
case KCAS_IOCTL_GET_CORE_POOL_COUNT: {
|
||||
struct kcas_core_pool_count *cmd_info;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
cmd_info->core_pool_count =
|
||||
ocf_mngt_core_pool_get_count(cas_ctx);
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, 0);
|
||||
}
|
||||
|
||||
case KCAS_IOCTL_GET_CORE_POOL_PATHS: {
|
||||
struct kcas_core_pool_path *cmd_info;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
retval = cache_mng_core_pool_get_paths(cmd_info);
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
}
|
||||
case KCAS_IOCTL_CORE_POOL_REMOVE: {
|
||||
struct kcas_core_pool_remove *cmd_info;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
retval = cache_mng_core_pool_remove(cmd_info);
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
}
|
||||
case KCAS_IOCTL_CACHE_CHECK_DEVICE: {
|
||||
struct kcas_cache_check_device *cmd_info;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
retval = cache_mng_cache_check_device(cmd_info);
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
}
|
||||
case KCAS_IOCTL_SET_CORE_PARAM: {
|
||||
struct kcas_set_core_param *cmd_info;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
retval = cache_mng_set_core_params(cmd_info);
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
}
|
||||
case KCAS_IOCTL_GET_CORE_PARAM: {
|
||||
struct kcas_get_core_param *cmd_info;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
retval = cache_mng_get_core_params(cmd_info);
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
}
|
||||
case KCAS_IOCTL_SET_CACHE_PARAM: {
|
||||
struct kcas_set_cache_param *cmd_info;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
retval = cache_mng_set_cache_params(cmd_info);
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
}
|
||||
case KCAS_IOCTL_GET_CACHE_PARAM: {
|
||||
struct kcas_get_cache_param *cmd_info;
|
||||
|
||||
GET_CMD_INFO(cmd_info, arg);
|
||||
|
||||
retval = cache_mng_get_cache_params(cmd_info);
|
||||
|
||||
RETURN_CMD_RESULT(cmd_info, arg, retval);
|
||||
}
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
15
modules/cas_cache/service_ui_ioctl.h
Normal file
15
modules/cas_cache/service_ui_ioctl.h
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef __SERVICE_UI_IOCTL_H__
|
||||
|
||||
#define __SERVICE_UI_IOCTL_H__
|
||||
|
||||
struct casdsk_disk;
|
||||
|
||||
long cas_service_ioctl_ctrl(struct file *filp, unsigned int cmd,
|
||||
unsigned long arg);
|
||||
|
||||
#endif
|
281
modules/cas_cache/threads.c
Normal file
281
modules/cas_cache/threads.c
Normal file
@ -0,0 +1,281 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "threads.h"
|
||||
#include "cas_cache.h"
|
||||
|
||||
#define MAX_THREAD_NAME_SIZE 16
|
||||
|
||||
struct cas_thread_info {
|
||||
atomic_t stop;
|
||||
struct completion compl;
|
||||
struct completion sync_compl;
|
||||
void *sync_data;
|
||||
wait_queue_head_t wq;
|
||||
atomic_t kicked;
|
||||
struct task_struct *thread;
|
||||
char name[MAX_THREAD_NAME_SIZE];
|
||||
bool running;
|
||||
};
|
||||
|
||||
static int _cas_io_queue_thread(void *data)
|
||||
{
|
||||
ocf_queue_t q = data;
|
||||
struct cas_thread_info *info;
|
||||
|
||||
BUG_ON(!q);
|
||||
|
||||
/* complete the creation of the thread */
|
||||
info = ocf_queue_get_priv(q);
|
||||
BUG_ON(!info);
|
||||
|
||||
DAEMONIZE(info->thread->comm);
|
||||
|
||||
complete(&info->compl);
|
||||
|
||||
/* Continue working until signaled to exit. */
|
||||
do {
|
||||
/* Wait until there are completed read misses from the HDDs,
|
||||
* or a stop.
|
||||
*/
|
||||
wait_event_interruptible(info->wq, ocf_queue_pending_io(q) ||
|
||||
atomic_read(&info->stop));
|
||||
|
||||
ocf_queue_run(q);
|
||||
|
||||
} while (!atomic_read(&info->stop) || ocf_queue_pending_io(q));
|
||||
|
||||
WARN(ocf_queue_pending_io(q), "Still pending IO requests\n");
|
||||
|
||||
/* If we get here, then thread was signalled to terminate.
|
||||
* So, let's complete and exit.
|
||||
*/
|
||||
complete_and_exit(&info->compl, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _cas_cleaner_complete(ocf_cleaner_t c, uint32_t interval)
|
||||
{
|
||||
struct cas_thread_info *info = ocf_cleaner_get_priv(c);
|
||||
uint32_t *ms = info->sync_data;
|
||||
|
||||
*ms = interval;
|
||||
complete(&info->sync_compl);
|
||||
}
|
||||
|
||||
static int _cas_cleaner_thread(void *data)
|
||||
{
|
||||
ocf_cleaner_t c = data;
|
||||
ocf_cache_t cache = ocf_cleaner_get_cache(c);
|
||||
struct cache_priv *cache_priv = ocf_cache_get_priv(cache);
|
||||
struct cas_thread_info *info;
|
||||
uint32_t ms;
|
||||
|
||||
BUG_ON(!c);
|
||||
|
||||
ENV_BUG_ON(!cache_priv);
|
||||
/* complete the creation of the thread */
|
||||
info = ocf_cleaner_get_priv(c);
|
||||
BUG_ON(!info);
|
||||
|
||||
DAEMONIZE(info->thread->comm);
|
||||
|
||||
complete(&info->compl);
|
||||
|
||||
info->sync_data = &ms;
|
||||
ocf_cleaner_set_cmpl(c, _cas_cleaner_complete);
|
||||
|
||||
do {
|
||||
init_completion(&info->sync_compl);
|
||||
ocf_cleaner_run(c, cache_priv->io_queues[smp_processor_id()]);
|
||||
wait_for_completion(&info->sync_compl);
|
||||
} while (0 == wait_event_interruptible_timeout(info->wq,
|
||||
atomic_read(&info->stop), msecs_to_jiffies(ms)));
|
||||
|
||||
complete_and_exit(&info->compl, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _cas_metadata_updater_thread(void *data)
|
||||
{
|
||||
ocf_metadata_updater_t mu = data;
|
||||
struct cas_thread_info *info;
|
||||
|
||||
BUG_ON(!mu);
|
||||
|
||||
/* complete the creation of the thread */
|
||||
info = ocf_metadata_updater_get_priv(mu);
|
||||
BUG_ON(!info);
|
||||
|
||||
DAEMONIZE(info->thread->comm);
|
||||
|
||||
complete(&info->compl);
|
||||
|
||||
do {
|
||||
if (atomic_read(&info->stop))
|
||||
break;
|
||||
|
||||
atomic_set(&info->kicked, 0);
|
||||
if (ocf_metadata_updater_run(mu))
|
||||
continue;
|
||||
|
||||
wait_event_interruptible(info->wq, atomic_read(&info->stop) ||
|
||||
atomic_read(&info->kicked));
|
||||
} while (true);
|
||||
|
||||
complete_and_exit(&info->compl, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _cas_create_thread(struct cas_thread_info **pinfo,
|
||||
int (*threadfn)(void *), void *priv, int cpu,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
struct cas_thread_info *info;
|
||||
struct task_struct *thread;
|
||||
va_list args;
|
||||
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
atomic_set(&info->stop, 0);
|
||||
init_completion(&info->compl);
|
||||
init_completion(&info->sync_compl);
|
||||
init_waitqueue_head(&info->wq);
|
||||
|
||||
va_start(args, fmt);
|
||||
vsnprintf(info->name, sizeof(info->name), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
thread = kthread_create(threadfn, priv, "%s", info->name);
|
||||
if (IS_ERR(thread)) {
|
||||
kfree(info);
|
||||
/* Propagate error code as PTR_ERR */
|
||||
return PTR_ERR(thread);
|
||||
}
|
||||
info->thread = thread;
|
||||
|
||||
/* Affinitize thread to core */
|
||||
if (cpu != CAS_CPUS_ALL)
|
||||
kthread_bind(thread, cpu);
|
||||
|
||||
if (pinfo)
|
||||
*pinfo = info;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static void _cas_start_thread(struct cas_thread_info *info)
|
||||
{
|
||||
wake_up_process(info->thread);
|
||||
wait_for_completion(&info->compl);
|
||||
|
||||
info->running = true;
|
||||
|
||||
printk(KERN_DEBUG "Thread %s started\n", info->name);
|
||||
}
|
||||
|
||||
static void _cas_stop_thread(struct cas_thread_info *info)
|
||||
{
|
||||
if (info->running && info->thread) {
|
||||
init_completion(&info->compl);
|
||||
atomic_set(&info->stop, 1);
|
||||
wake_up(&info->wq);
|
||||
wait_for_completion(&info->compl);
|
||||
printk(KERN_DEBUG "Thread %s stopped\n", info->name);
|
||||
}
|
||||
kfree(info);
|
||||
}
|
||||
|
||||
int cas_create_queue_thread(ocf_queue_t q, int cpu)
|
||||
{
|
||||
struct cas_thread_info *info;
|
||||
ocf_cache_t cache = ocf_queue_get_cache(q);
|
||||
int result;
|
||||
|
||||
result = _cas_create_thread(&info, _cas_io_queue_thread, q, cpu,
|
||||
"cas_io_%s_%d", ocf_cache_get_name(cache), cpu);
|
||||
if (!result) {
|
||||
ocf_queue_set_priv(q, info);
|
||||
_cas_start_thread(info);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void cas_kick_queue_thread(ocf_queue_t q)
|
||||
{
|
||||
struct cas_thread_info *info = ocf_queue_get_priv(q);
|
||||
wake_up(&info->wq);
|
||||
}
|
||||
|
||||
|
||||
void cas_stop_queue_thread(ocf_queue_t q)
|
||||
{
|
||||
struct cas_thread_info *info = ocf_queue_get_priv(q);
|
||||
ocf_queue_set_priv(q, NULL);
|
||||
_cas_stop_thread(info);
|
||||
}
|
||||
|
||||
int cas_create_cleaner_thread(ocf_cleaner_t c)
|
||||
{
|
||||
struct cas_thread_info *info;
|
||||
ocf_cache_t cache = ocf_cleaner_get_cache(c);
|
||||
int result;
|
||||
|
||||
result = _cas_create_thread(&info, _cas_cleaner_thread, c,
|
||||
CAS_CPUS_ALL, "cas_clean_%d",
|
||||
ocf_cache_get_id(cache));
|
||||
if (!result) {
|
||||
ocf_cleaner_set_priv(c, info);
|
||||
_cas_start_thread(info);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void cas_stop_cleaner_thread(ocf_cleaner_t c)
|
||||
{
|
||||
struct cas_thread_info *info = ocf_cleaner_get_priv(c);
|
||||
ocf_cleaner_set_priv(c, NULL);
|
||||
_cas_stop_thread(info);
|
||||
}
|
||||
|
||||
int cas_create_metadata_updater_thread(ocf_metadata_updater_t mu)
|
||||
{
|
||||
struct cas_thread_info *info;
|
||||
int result;
|
||||
|
||||
result = _cas_create_thread(&info, _cas_metadata_updater_thread,
|
||||
mu, CAS_CPUS_ALL, "ocf_metadata_updater_%d",
|
||||
ocf_cache_get_id(ocf_metadata_updater_get_cache(mu)));
|
||||
if (!result) {
|
||||
ocf_metadata_updater_set_priv(mu, info);
|
||||
_cas_start_thread(info);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void cas_kick_metadata_updater_thread(ocf_metadata_updater_t mu)
|
||||
{
|
||||
struct cas_thread_info *info = ocf_metadata_updater_get_priv(mu);
|
||||
atomic_set(&info->kicked, 1);
|
||||
wake_up(&info->wq);
|
||||
}
|
||||
|
||||
|
||||
void cas_stop_metadata_updater_thread(ocf_metadata_updater_t mu)
|
||||
{
|
||||
struct cas_thread_info *info = ocf_metadata_updater_get_priv(mu);
|
||||
ocf_metadata_updater_set_priv(mu, NULL);
|
||||
_cas_stop_thread(info);
|
||||
}
|
||||
|
26
modules/cas_cache/threads.h
Normal file
26
modules/cas_cache/threads.h
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __THREADS_H__
|
||||
#define __THREADS_H__
|
||||
|
||||
#include "ocf/ocf.h"
|
||||
#include "linux_kernel_version.h"
|
||||
|
||||
#define CAS_CPUS_ALL -1
|
||||
|
||||
int cas_create_queue_thread(ocf_queue_t q, int cpu);
|
||||
void cas_kick_queue_thread(ocf_queue_t q);
|
||||
void cas_stop_queue_thread(ocf_queue_t q);
|
||||
|
||||
int cas_create_cleaner_thread(ocf_cleaner_t c);
|
||||
void cas_stop_cleaner_thread(ocf_cleaner_t c);
|
||||
|
||||
int cas_create_metadata_updater_thread(ocf_metadata_updater_t mu);
|
||||
void cas_kick_metadata_updater_thread(ocf_metadata_updater_t mu);
|
||||
void cas_stop_metadata_updater_thread(ocf_metadata_updater_t mu);
|
||||
|
||||
#endif /* __THREADS_H__ */
|
13
modules/cas_cache/utils/cas_cache_utils.h
Normal file
13
modules/cas_cache/utils/cas_cache_utils.h
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __CAS_UTILS_H__
|
||||
#define __CAS_UTILS_H__
|
||||
|
||||
#include "utils_nvme.h"
|
||||
#include "utils_properties.h"
|
||||
|
||||
#endif /* __CAS_UTILS_H__ */
|
22
modules/cas_cache/utils/utils_blk.c
Normal file
22
modules/cas_cache/utils/utils_blk.c
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "utils_blk.h"
|
||||
|
||||
int cas_blk_get_part_count(struct block_device *bdev)
|
||||
{
|
||||
struct disk_part_tbl *ptbl;
|
||||
int i, count = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
ptbl = rcu_dereference(bdev->bd_disk->part_tbl);
|
||||
for (i = 0; i < ptbl->len; ++i) {
|
||||
if (rcu_access_pointer(ptbl->part[i]))
|
||||
count++;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return count;
|
||||
}
|
14
modules/cas_cache/utils/utils_blk.h
Normal file
14
modules/cas_cache/utils/utils_blk.h
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef UTILS_BLK_H_
|
||||
#define UTILS_BLK_H_
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/genhd.h>
|
||||
|
||||
int cas_blk_get_part_count(struct block_device *bdev);
|
||||
|
||||
#endif /* UTILS_BLK_H_ */
|
130
modules/cas_cache/utils/utils_data.c
Normal file
130
modules/cas_cache/utils/utils_data.c
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "cas_cache.h"
|
||||
|
||||
/**
|
||||
* This function locates index of IO vec from given vecs array where byte at
|
||||
* offset is located. When found it returns its index and byte offset within
|
||||
* this vec.
|
||||
* @param vecs IO vector array to be searched
|
||||
* @param vec_num number of items in IO vector array
|
||||
* @param offset byte offset to be found
|
||||
* @param offset_in_vec byte offset within found IO vec
|
||||
* @return vec index if it lies within specified buffer, otherwise -1
|
||||
*/
|
||||
static int get_starting_vec(struct bio_vec *vecs, uint64_t vecs_num,
|
||||
uint64_t offset, uint64_t *offset_in_vec)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < vecs_num; i++) {
|
||||
if (vecs[i].bv_len > offset) {
|
||||
if (offset_in_vec != NULL)
|
||||
*offset_in_vec = offset;
|
||||
return i;
|
||||
}
|
||||
offset -= vecs[i].bv_len;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint64_t cas_data_cpy(struct bio_vec *dst, uint64_t dst_num,
|
||||
struct bio_vec *src, uint64_t src_num,
|
||||
uint64_t to, uint64_t from, uint64_t bytes)
|
||||
{
|
||||
uint64_t i, j, dst_len, src_len, to_copy;
|
||||
uint64_t dst_off, src_off;
|
||||
uint64_t written = 0;
|
||||
int ret;
|
||||
void *dst_p, *src_p;
|
||||
struct bio_vec *curr_dst, *curr_src;
|
||||
|
||||
/* Locate vec idx and offset in dst vec array */
|
||||
ret = get_starting_vec(dst, dst_num, to, &to);
|
||||
if (ret < 0) {
|
||||
CAS_PRINT_RL(KERN_INFO "llu dst buffer too small "
|
||||
"to_offset=%llu bytes=%llu", to, bytes);
|
||||
return 0;
|
||||
}
|
||||
j = ret;
|
||||
|
||||
/* Locate vec idx and offset in src vec array */
|
||||
ret = get_starting_vec(src, src_num, from, &from);
|
||||
if (ret < 0) {
|
||||
CAS_PRINT_RL(KERN_INFO "llu src buffer too small "
|
||||
"from_offset=%llu bytes=%llu", from, bytes);
|
||||
return 0;
|
||||
}
|
||||
i = ret;
|
||||
|
||||
curr_dst = &dst[j];
|
||||
curr_src = &src[i];
|
||||
|
||||
dst_off = curr_dst->bv_offset + to;
|
||||
dst_len = curr_dst->bv_len - to;
|
||||
|
||||
src_off = curr_src->bv_offset + from;
|
||||
src_len = curr_src->bv_len - from;
|
||||
|
||||
while (written < bytes) {
|
||||
dst_p = page_address(curr_dst->bv_page) + dst_off;
|
||||
src_p = page_address(curr_src->bv_page) + src_off;
|
||||
|
||||
to_copy = src_len > dst_len ? dst_len : src_len;
|
||||
|
||||
/* Prevent from copying too much*/
|
||||
if ((written + to_copy) > bytes)
|
||||
to_copy = bytes - written;
|
||||
|
||||
memcpy(dst_p, src_p, to_copy);
|
||||
written += to_copy;
|
||||
|
||||
if (written == bytes)
|
||||
break;
|
||||
|
||||
/* Setup new len and offset. */
|
||||
dst_off += to_copy;
|
||||
dst_len -= to_copy;
|
||||
|
||||
src_off += to_copy;
|
||||
src_len -= to_copy;
|
||||
|
||||
/* Go to next src buffer */
|
||||
if (src_len == 0) {
|
||||
i++;
|
||||
|
||||
/* Setup new len and offset. */
|
||||
if (i < src_num) {
|
||||
curr_src = &src[i];
|
||||
src_off = curr_src->bv_offset;
|
||||
src_len = curr_src->bv_len;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Go to next dst buffer */
|
||||
if (dst_len == 0) {
|
||||
j++;
|
||||
|
||||
if (j < dst_num) {
|
||||
curr_dst = &dst[j];
|
||||
dst_off = curr_dst->bv_offset;
|
||||
dst_len = curr_dst->bv_len;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (written != bytes) {
|
||||
CAS_PRINT_RL(KERN_INFO "Written bytes not equal requested bytes "
|
||||
"(written=%llu; requested=%llu)", written, bytes);
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
31
modules/cas_cache/utils/utils_data.h
Normal file
31
modules/cas_cache/utils/utils_data.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef UTILS_DATA_H_
|
||||
#define UTILS_DATA_H_
|
||||
|
||||
/**
|
||||
* @brief Copy data from a data vector to another one
|
||||
*
|
||||
* This function copies number of bytes from source IO vector to destination
|
||||
* IO vector. It starts coping to specified offset in destination IO vector. If
|
||||
* there is not enough space it will return number of bytes that was
|
||||
* successfully copied.
|
||||
*
|
||||
* @param dst destination IO vector
|
||||
* @param dst_num size of destination IO vector
|
||||
* @param src source IO vector
|
||||
* @param src_num size of source IO vector
|
||||
* @param to dst offset where write to will start
|
||||
* @param from src offset where write from will start
|
||||
* @param bytes number of bytes to be copied
|
||||
*
|
||||
* @return number of bytes written from src to dst
|
||||
*/
|
||||
uint64_t cas_data_cpy(struct bio_vec *dst, uint64_t dst_num,
|
||||
struct bio_vec *src, uint64_t src_num,
|
||||
uint64_t to, uint64_t from, uint64_t bytes);
|
||||
|
||||
#endif /* UTILS_DATA_H_ */
|
78
modules/cas_cache/utils/utils_gc.c
Normal file
78
modules/cas_cache/utils/utils_gc.c
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "utils_gc.h"
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#if defined (CAS_GARBAGE_COLLECTOR)
|
||||
struct cas_vfree_item {
|
||||
struct llist_head list;
|
||||
struct work_struct ws;
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct cas_vfree_item, cas_vfree_item);
|
||||
|
||||
static atomic_t freed = ATOMIC_INIT(0);
|
||||
|
||||
static void cas_garbage_collector(struct work_struct *w)
|
||||
{
|
||||
struct cas_vfree_item *item = container_of(w, struct cas_vfree_item,
|
||||
ws);
|
||||
struct llist_node *llnode = llist_del_all(&item->list);
|
||||
|
||||
while (llnode) {
|
||||
void *item = llnode;
|
||||
|
||||
llnode = llnode->next;
|
||||
atomic_dec(&freed);
|
||||
vfree(item);
|
||||
}
|
||||
}
|
||||
|
||||
void cas_vfree(const void *addr)
|
||||
{
|
||||
struct cas_vfree_item *item = this_cpu_ptr(&cas_vfree_item);
|
||||
|
||||
atomic_inc(&freed);
|
||||
|
||||
if (llist_add((struct llist_node *)addr, &item->list))
|
||||
schedule_work(&item->ws);
|
||||
}
|
||||
|
||||
void cas_garbage_collector_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for_each_possible_cpu(i) {
|
||||
struct cas_vfree_item *item;
|
||||
|
||||
item = &per_cpu(cas_vfree_item, i);
|
||||
init_llist_head(&item->list);
|
||||
INIT_WORK(&item->ws, cas_garbage_collector);
|
||||
}
|
||||
}
|
||||
|
||||
void cas_garbage_collector_deinit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for_each_possible_cpu(i) {
|
||||
struct cas_vfree_item *item;
|
||||
|
||||
item = &per_cpu(cas_vfree_item, i);
|
||||
while (work_pending(&item->ws))
|
||||
schedule();
|
||||
}
|
||||
|
||||
WARN(atomic_read(&freed) != 0,
|
||||
OCF_PREFIX_SHORT" Not all memory deallocated\n");
|
||||
}
|
||||
#else
|
||||
void cas_garbage_collector_init(void) {};
|
||||
|
||||
void cas_garbage_collector_deinit(void) {};
|
||||
|
||||
void cas_vfree(const void *addr) { vfree(addr); };
|
||||
#endif
|
16
modules/cas_cache/utils/utils_gc.h
Normal file
16
modules/cas_cache/utils/utils_gc.h
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef UTILS_GC_H_
|
||||
#define UTILS_GC_H_
|
||||
|
||||
|
||||
void cas_garbage_collector_init(void);
|
||||
|
||||
void cas_garbage_collector_deinit(void);
|
||||
|
||||
void cas_vfree(const void *addr);
|
||||
|
||||
#endif /* UTILS_GC_H_ */
|
583
modules/cas_cache/utils/utils_nvme.c
Normal file
583
modules/cas_cache/utils/utils_nvme.c
Normal file
@ -0,0 +1,583 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#if defined(CAS_NVME_PARTIAL)
|
||||
|
||||
#include "cas_cache.h"
|
||||
#include "utils_nvme.h"
|
||||
#include "utils_blk.h"
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/file.h>
|
||||
|
||||
|
||||
int cas_nvme_get_nsid(struct block_device *bdev, unsigned int *nsid)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* Maximum NSID is 0xFFFFFFFF, so theoretically there is no free
|
||||
* room for error code. However it's unlikely that there will ever
|
||||
* be device with such number of namespaces, so we treat this value
|
||||
* as it was signed. Then in case of negative value we interpret it
|
||||
* as an error code. Moreover in case of error we can be sure, that
|
||||
* we deal with non-NVMe device, because this ioctl should never
|
||||
* fail with NVMe driver.
|
||||
*/
|
||||
ret = ioctl_by_bdev(bdev, NVME_IOCTL_ID, (unsigned long)NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*nsid = (unsigned int)ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define NVME_ID_CNS_NS 0x00
|
||||
#define NVME_ID_CNS_CTRL 0x01
|
||||
|
||||
int cas_nvme_identify_ns(struct block_device *bdev, unsigned int nsid,
|
||||
struct nvme_id_ns *ns)
|
||||
{
|
||||
struct nvme_admin_cmd cmd = { };
|
||||
unsigned long __user buffer;
|
||||
int ret = 0;
|
||||
|
||||
buffer = cas_vm_mmap(NULL, 0, sizeof(*ns));
|
||||
if (IS_ERR((void *)buffer))
|
||||
return PTR_ERR((void *)buffer);
|
||||
|
||||
cmd.opcode = nvme_admin_identify;
|
||||
cmd.nsid = cpu_to_le32(nsid);
|
||||
cmd.addr = (__u64)buffer;
|
||||
cmd.data_len = sizeof(*ns);
|
||||
cmd.cdw10 = NVME_ID_CNS_NS;
|
||||
ret = ioctl_by_bdev(bdev, NVME_IOCTL_ADMIN_CMD, (unsigned long)&cmd);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = copy_from_user(ns, (void *)buffer, sizeof(*ns));
|
||||
if (ret > 0)
|
||||
ret = -EINVAL;
|
||||
out:
|
||||
cas_vm_munmap(buffer, sizeof(*ns));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cas_nvme_identify_ns_contorller(struct file *file, struct nvme_id_ns *ns)
|
||||
{
|
||||
struct nvme_admin_cmd cmd = { };
|
||||
unsigned long __user buffer;
|
||||
mm_segment_t old_fs;
|
||||
int ret = 0;
|
||||
|
||||
buffer = cas_vm_mmap(NULL, 0, sizeof(*ns));
|
||||
if (IS_ERR((void *)buffer))
|
||||
return PTR_ERR((void *)buffer);
|
||||
|
||||
cmd.opcode = nvme_admin_identify;
|
||||
cmd.nsid = 1;
|
||||
cmd.addr = (__u64)buffer;
|
||||
cmd.data_len = sizeof(*ns);
|
||||
cmd.cdw10 = NVME_ID_CNS_NS;
|
||||
|
||||
old_fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
ret = file->f_op->unlocked_ioctl(file,
|
||||
NVME_IOCTL_ADMIN_CMD, (unsigned long)&cmd);
|
||||
set_fs(old_fs);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = copy_from_user(ns, (void *)buffer, sizeof(*ns));
|
||||
if (ret > 0)
|
||||
ret = -EINVAL;
|
||||
out:
|
||||
cas_vm_munmap(buffer, sizeof(*ns));
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(CAS_NVME_FULL)
|
||||
|
||||
#define FORMAT_WORKAROUND_NOT_NEED 0
|
||||
#define FORMAT_WORKAROUND_NEED 1
|
||||
|
||||
static int __cas_nvme_check_fw(struct nvme_id_ctrl *id_ctrl)
|
||||
{
|
||||
/*
|
||||
* If firmware is older then 8DV101H0 we need do
|
||||
* workaround - make format twice. We need to compare
|
||||
* only 5 last characters.
|
||||
*/
|
||||
|
||||
return (strncmp(&id_ctrl->fr[3], "101H0", 5) < 0) ?
|
||||
FORMAT_WORKAROUND_NEED :
|
||||
FORMAT_WORKAROUND_NOT_NEED;
|
||||
}
|
||||
|
||||
int cas_nvme_identify_ctrl(struct block_device *bdev,
|
||||
struct nvme_id_ctrl *id_ctrl)
|
||||
{
|
||||
struct nvme_admin_cmd cmd = { };
|
||||
unsigned long __user buffer;
|
||||
int ret = 0;
|
||||
|
||||
buffer = cas_vm_mmap(NULL, 0, sizeof(*id_ctrl));
|
||||
if (IS_ERR((void *)buffer))
|
||||
return PTR_ERR((void *)buffer);
|
||||
|
||||
cmd.opcode = nvme_admin_identify;
|
||||
cmd.addr = (__u64)buffer;
|
||||
cmd.data_len = sizeof(*id_ctrl);
|
||||
cmd.cdw10 = NVME_ID_CNS_CTRL;
|
||||
|
||||
ret = ioctl_by_bdev(bdev, NVME_IOCTL_ADMIN_CMD, (unsigned long)&cmd);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = copy_from_user(id_ctrl, (void *)buffer, sizeof(*id_ctrl));
|
||||
if (ret > 0)
|
||||
ret = -EINVAL;
|
||||
|
||||
out:
|
||||
cas_vm_munmap(buffer, sizeof(*id_ctrl));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _cas_nvme_format_bdev(struct block_device *bdev, unsigned int nsid,
|
||||
int lbaf, int ms)
|
||||
{
|
||||
struct nvme_admin_cmd cmd = { };
|
||||
|
||||
cmd.opcode = nvme_admin_format_nvm;
|
||||
cmd.nsid = nsid;
|
||||
cmd.cdw10 = lbaf | ms<<4;
|
||||
cmd.timeout_ms = 1200000;
|
||||
return ioctl_by_bdev(bdev, NVME_IOCTL_ADMIN_CMD, (unsigned long)&cmd);
|
||||
}
|
||||
|
||||
static int _cas_nvme_controller_identify(struct file *character_device_file,
|
||||
unsigned long __user buffer)
|
||||
{
|
||||
struct nvme_admin_cmd cmd = { };
|
||||
mm_segment_t old_fs;
|
||||
int ret;
|
||||
|
||||
old_fs = get_fs();
|
||||
|
||||
cmd.opcode = nvme_admin_identify;
|
||||
cmd.nsid = 0;
|
||||
cmd.addr = (__u64)buffer;
|
||||
/* 1 - identify contorller, 0 - identify namespace */
|
||||
cmd.cdw10 = 1;
|
||||
cmd.data_len = 0x1000;
|
||||
|
||||
set_fs(KERNEL_DS);
|
||||
ret = character_device_file->f_op->unlocked_ioctl(character_device_file,
|
||||
NVME_IOCTL_ADMIN_CMD, (unsigned long)&cmd);
|
||||
set_fs(old_fs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _cas_nvme_format_controller(struct file *character_device_file,
|
||||
int lbaf, bool sbnsupp)
|
||||
{
|
||||
struct nvme_admin_cmd cmd = { };
|
||||
mm_segment_t old_fs;
|
||||
int ret;
|
||||
|
||||
old_fs = get_fs();
|
||||
|
||||
/* Send format command to device */
|
||||
cmd.opcode = nvme_admin_format_nvm;
|
||||
cmd.nsid = 0xFFFFFFFF;
|
||||
cmd.cdw10 = lbaf | sbnsupp << 4;
|
||||
cmd.timeout_ms = 120000;
|
||||
cmd.addr = 0;
|
||||
|
||||
set_fs(KERNEL_DS);
|
||||
ret = character_device_file->f_op->unlocked_ioctl(character_device_file,
|
||||
NVME_IOCTL_ADMIN_CMD, (unsigned long)&cmd);
|
||||
set_fs(old_fs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int find_lbaf(struct nvme_lbaf *lbaf, int cnt, int atomic)
|
||||
{
|
||||
int ms = atomic ? 8 : 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= cnt; ++i)
|
||||
if (lbaf[i].ms == ms && lbaf[i].ds == 9)
|
||||
return i;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* context for async probe */
|
||||
struct _probe_context
|
||||
{
|
||||
struct completion cmpl;
|
||||
struct ocf_metadata_probe_status status;
|
||||
int error;
|
||||
};
|
||||
|
||||
static void _cas_nvme_probe_cmpl(void *priv, int error,
|
||||
struct ocf_metadata_probe_status *status)
|
||||
{
|
||||
struct _probe_context *ctx = (struct _probe_context*)priv;
|
||||
|
||||
ctx->error = error;
|
||||
if (!error) {
|
||||
ctx->status = *status;
|
||||
}
|
||||
|
||||
complete(&ctx->cmpl);
|
||||
}
|
||||
|
||||
static int _cas_nvme_preformat_check(struct block_device *bdev, int force)
|
||||
{
|
||||
ocf_volume_t volume;
|
||||
struct _probe_context probe_ctx;
|
||||
int ret = 0;
|
||||
|
||||
if (bdev != bdev->bd_contains)
|
||||
return -KCAS_ERR_A_PART;
|
||||
|
||||
if (cas_blk_get_part_count(bdev) > 1 && !force)
|
||||
return -KCAS_ERR_CONTAINS_PART;
|
||||
|
||||
ret = cas_blk_open_volume_by_bdev(&volume, bdev);
|
||||
if (ret == -KCAS_ERR_NVME_BAD_FORMAT) {
|
||||
/* Current format is not supported by CAS, so we can be sure
|
||||
* that there is no dirty data. Do format
|
||||
*/
|
||||
return 0;
|
||||
} else if (ret) {
|
||||
/* An error occurred, stop processing */
|
||||
return ret;
|
||||
}
|
||||
|
||||
init_completion(&probe_ctx.cmpl);
|
||||
ocf_metadata_probe(cas_ctx, volume, _cas_nvme_probe_cmpl, &probe_ctx);
|
||||
if (wait_for_completion_interruptible(&probe_ctx.cmpl)) {
|
||||
ocf_volume_close(volume);
|
||||
return -OCF_ERR_FLUSHING_INTERRUPTED;
|
||||
}
|
||||
|
||||
if (probe_ctx.error == -ENODATA) {
|
||||
/* Cache was not detected on this device
|
||||
* NVMe can be formated
|
||||
*/
|
||||
ret = 0;
|
||||
} else if (probe_ctx.error == -EBUSY) {
|
||||
ret = -OCF_ERR_NOT_OPEN_EXC;
|
||||
} else if (probe_ctx.error) {
|
||||
/* Some error occurred, we do not have sure about clean cache */
|
||||
ret = -KCAS_ERR_FORMAT_FAILED;
|
||||
} else {
|
||||
/* Check if cache was closed in proper way */
|
||||
if (!probe_ctx.status.clean_shutdown ||
|
||||
probe_ctx.status.cache_dirty) {
|
||||
/* Dirty shutdown */
|
||||
ret = -KCAS_ERR_DIRTY_EXISTS_NVME;
|
||||
}
|
||||
|
||||
if (force) {
|
||||
/* Force overwrites dirty shutdown */
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ocf_volume_close(volume);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _cas_nvme_format_namespace_by_path(const char *device_path,
|
||||
int metadata_mode, int force)
|
||||
{
|
||||
struct nvme_id_ns *ns;
|
||||
struct nvme_id_ctrl *id;
|
||||
|
||||
unsigned int nsid, sbnsupp = 0;
|
||||
int best_lbaf = 0;
|
||||
int ret = 0;
|
||||
struct block_device *bdev;
|
||||
char holder[] = "CAS FORMAT\n";
|
||||
|
||||
ns = kmalloc(sizeof(*ns), GFP_KERNEL);
|
||||
if (!ns)
|
||||
return -OCF_ERR_NO_MEM;
|
||||
|
||||
id = kmalloc(sizeof(*id), GFP_KERNEL);
|
||||
if (!id) {
|
||||
ret = -OCF_ERR_NO_MEM;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
bdev = OPEN_BDEV_EXCLUSIVE(device_path,
|
||||
FMODE_READ | FMODE_WRITE | FMODE_EXCL, holder);
|
||||
if (IS_ERR(bdev)) {
|
||||
if (PTR_ERR(bdev) == -EBUSY)
|
||||
ret = -OCF_ERR_NOT_OPEN_EXC;
|
||||
else
|
||||
ret = -OCF_ERR_INVAL_VOLUME_TYPE;
|
||||
|
||||
goto out1;
|
||||
}
|
||||
|
||||
ret = cas_nvme_get_nsid(bdev, &nsid);
|
||||
if (ret < 0) {
|
||||
ret = -KCAS_ERR_NOT_NVME;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
ret = _cas_nvme_preformat_check(bdev, force);
|
||||
if (ret)
|
||||
goto out2;
|
||||
|
||||
ret = cas_nvme_identify_ns(bdev, nsid, ns);
|
||||
if (ret < 0) {
|
||||
ret = -KCAS_ERR_FORMAT_FAILED;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
if (metadata_mode == CAS_METADATA_MODE_NORMAL) {
|
||||
best_lbaf = find_lbaf(ns->lbaf, ns->nlbaf, 0);
|
||||
sbnsupp = 0;
|
||||
} else if (metadata_mode == CAS_METADATA_MODE_ATOMIC) {
|
||||
best_lbaf = find_lbaf(ns->lbaf, ns->nlbaf, 1);
|
||||
sbnsupp = !(ns->mc & (1<<1));
|
||||
}
|
||||
|
||||
if (best_lbaf < 0) {
|
||||
ret = -KCAS_ERR_FORMAT_FAILED;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
ret = cas_nvme_identify_ctrl(bdev, id);
|
||||
if (ret < 0) {
|
||||
ret = -KCAS_ERR_FORMAT_FAILED;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
if (__cas_nvme_check_fw(id) == FORMAT_WORKAROUND_NEED) {
|
||||
/*
|
||||
* If firmware is older then 8DV101H0 we need do
|
||||
* workaround - make format twice.
|
||||
*/
|
||||
ret = _cas_nvme_format_bdev(bdev, nsid, best_lbaf, sbnsupp);
|
||||
if (ret)
|
||||
goto out2;
|
||||
}
|
||||
|
||||
ret = _cas_nvme_format_bdev(bdev, nsid, best_lbaf, sbnsupp);
|
||||
if (ret)
|
||||
goto out2;
|
||||
|
||||
ret = ioctl_by_bdev(bdev, BLKRRPART, (unsigned long)NULL);
|
||||
out2:
|
||||
CLOSE_BDEV_EXCLUSIVE(bdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL);
|
||||
out1:
|
||||
kfree(id);
|
||||
kfree(ns);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int _cas_nvme_get_bdev_from_controller(struct block_device **bdev,
|
||||
int major, int minor, int namespace_number)
|
||||
{
|
||||
mm_segment_t old_fs;
|
||||
char *sys_path;
|
||||
struct file *file;
|
||||
char readbuffer[12] = {0};
|
||||
char holder[] = "CAS FORMAT\n";
|
||||
int ret = 0;
|
||||
|
||||
sys_path = kzalloc(sizeof(char)*MAX_STR_LEN, GFP_KERNEL);
|
||||
if (!sys_path)
|
||||
return -OCF_ERR_NO_MEM;
|
||||
|
||||
sprintf(sys_path, "/sys/dev/char/%d:%d/nvme%dn%d/dev",
|
||||
major, minor, minor, namespace_number);
|
||||
|
||||
file = filp_open(sys_path, O_RDONLY, 0);
|
||||
kfree(sys_path);
|
||||
if (IS_ERR(file))
|
||||
return -KCAS_ERR_FORMAT_FAILED;
|
||||
|
||||
old_fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
ret = file->f_op->read(file, readbuffer, sizeof(readbuffer),
|
||||
&file->f_pos);
|
||||
set_fs(old_fs);
|
||||
filp_close(file, 0);
|
||||
if (ret < 0)
|
||||
return -KCAS_ERR_FORMAT_FAILED;
|
||||
|
||||
ret = sscanf(readbuffer, "%d:%d", &major, &minor);
|
||||
if (ret < 0)
|
||||
return -KCAS_ERR_FORMAT_FAILED;
|
||||
|
||||
*bdev = blkdev_get_by_dev(MKDEV(major, minor),
|
||||
FMODE_READ | FMODE_WRITE | FMODE_EXCL, holder);
|
||||
if (IS_ERR(*bdev))
|
||||
return -OCF_ERR_INVAL_VOLUME_TYPE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _cas_nvme_format_character_device(const char *device_path,
|
||||
int metadata_mode, int force)
|
||||
{
|
||||
mm_segment_t old_fs;
|
||||
int ret;
|
||||
struct file *character_device_file = NULL;
|
||||
struct nvme_id_ctrl *ctrl;
|
||||
unsigned long __user buffer;
|
||||
struct kstat *stat;
|
||||
struct block_device **ndev = NULL;
|
||||
int i;
|
||||
struct nvme_id_ns *ns;
|
||||
int best_lbaf = 0;
|
||||
int sbnsupp = 0;
|
||||
|
||||
ctrl = kzalloc(sizeof(struct nvme_id_ctrl), GFP_KERNEL);
|
||||
buffer = cas_vm_mmap(NULL, 0, sizeof(*ctrl));
|
||||
stat = kmalloc(sizeof(struct kstat), GFP_KERNEL);
|
||||
ns = kmalloc(sizeof(*ns), GFP_KERNEL);
|
||||
|
||||
old_fs = get_fs();
|
||||
|
||||
if (!ctrl || !buffer || !stat || !ns) {
|
||||
ret = -OCF_ERR_NO_MEM;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
character_device_file = filp_open(device_path, O_RDWR | O_EXCL, 0);
|
||||
if (IS_ERR(character_device_file)) {
|
||||
ret = -OCF_ERR_INVAL_VOLUME_TYPE;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
ret = _cas_nvme_controller_identify(character_device_file, buffer);
|
||||
if (ret < 0) {
|
||||
ret = KCAS_ERR_FORMAT_FAILED;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
ret = copy_from_user(ctrl, (void *)buffer, sizeof(*ctrl));
|
||||
if (ret)
|
||||
goto out1;
|
||||
|
||||
ndev = kmalloc_array(ctrl->nn, sizeof(struct block_device), GFP_KERNEL);
|
||||
if (!ndev) {
|
||||
ret = -OCF_ERR_NO_MEM;
|
||||
goto out1;
|
||||
}
|
||||
|
||||
set_fs(KERNEL_DS);
|
||||
ret = vfs_stat(device_path, stat);
|
||||
set_fs(old_fs);
|
||||
if (ret)
|
||||
goto out1;
|
||||
|
||||
for (i = 1; i <= ctrl->nn; i++) {
|
||||
ret = _cas_nvme_get_bdev_from_controller(&ndev[i-1],
|
||||
MAJOR(stat->rdev), MINOR(stat->rdev), i);
|
||||
if (ret) {
|
||||
i--;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = _cas_nvme_preformat_check(ndev[i-1], force);
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = cas_nvme_identify_ns_contorller(character_device_file, ns);
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
|
||||
if (metadata_mode == CAS_METADATA_MODE_NORMAL) {
|
||||
best_lbaf = find_lbaf(ns->lbaf, ns->nlbaf, 0);
|
||||
sbnsupp = 0;
|
||||
} else if (metadata_mode == CAS_METADATA_MODE_ATOMIC) {
|
||||
best_lbaf = find_lbaf(ns->lbaf, ns->nlbaf, 1);
|
||||
sbnsupp = !(ns->mc & (1<<1));
|
||||
}
|
||||
|
||||
if (best_lbaf < 0) {
|
||||
ret = -KCAS_ERR_FORMAT_FAILED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (__cas_nvme_check_fw(ctrl) == FORMAT_WORKAROUND_NEED) {
|
||||
/*
|
||||
* If firmware is older then 8DV101H0 we need do
|
||||
* workaround - make format twice.
|
||||
*/
|
||||
ret = _cas_nvme_format_controller(character_device_file,
|
||||
best_lbaf, sbnsupp);
|
||||
if (ret < 0) {
|
||||
ret = -KCAS_ERR_FORMAT_FAILED;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
ret = _cas_nvme_format_controller(character_device_file,
|
||||
best_lbaf, sbnsupp);
|
||||
if (ret < 0)
|
||||
ret = -KCAS_ERR_FORMAT_FAILED;
|
||||
|
||||
cleanup:
|
||||
for (i = i-1; i >= 1; i--) {
|
||||
ret |= ioctl_by_bdev(ndev[i-1], BLKRRPART, (unsigned long)NULL);
|
||||
blkdev_put(ndev[i-1], FMODE_READ | FMODE_WRITE | FMODE_EXCL);
|
||||
}
|
||||
|
||||
out1:
|
||||
kfree(ndev);
|
||||
kfree(ctrl);
|
||||
kfree(stat);
|
||||
|
||||
kfree(ns);
|
||||
cas_vm_munmap(buffer, sizeof(buffer));
|
||||
filp_close(character_device_file, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cas_nvme_format_optimal(const char *device_path, int metadata_mode,
|
||||
int force)
|
||||
{
|
||||
int ret;
|
||||
uint8_t type;
|
||||
|
||||
ret = cas_blk_identify_type(device_path, &type);
|
||||
if (ret == -OCF_ERR_INVAL_VOLUME_TYPE) {
|
||||
/* An error occurred, stop processing */
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (type == BLOCK_DEVICE_VOLUME || type == ATOMIC_DEVICE_VOLUME) {
|
||||
ret = _cas_nvme_format_namespace_by_path(device_path,
|
||||
metadata_mode, force);
|
||||
} else if (type == NVME_CONTROLLER && false) {
|
||||
/*
|
||||
* TODO(rbaldyga): Make it safe with NVMe drives that do not
|
||||
* handle format change properly.
|
||||
*/
|
||||
ret = _cas_nvme_format_character_device(device_path,
|
||||
metadata_mode, force);
|
||||
} else {
|
||||
ret = -OCF_ERR_INVAL_VOLUME_TYPE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
38
modules/cas_cache/utils/utils_nvme.h
Normal file
38
modules/cas_cache/utils/utils_nvme.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef UTILS_NVME_H_
|
||||
#define UTILS_NVME_H_
|
||||
|
||||
#if defined(CAS_UAPI_NVME)
|
||||
#include <uapi/nvme.h>
|
||||
#endif
|
||||
|
||||
#if defined(CAS_UAPI_LINUX_NVME)
|
||||
#include <uapi/linux/nvme.h>
|
||||
#endif
|
||||
|
||||
#if defined(CAS_UAPI_LINUX_NVME_IOCTL)
|
||||
#include <uapi/linux/nvme_ioctl.h>
|
||||
#endif
|
||||
|
||||
#if defined(CAS_NVME_PARTIAL)
|
||||
|
||||
#include <linux/nvme.h>
|
||||
|
||||
int cas_nvme_get_nsid(struct block_device *bdev, unsigned int *nsid);
|
||||
int cas_nvme_identify_ns(struct block_device *bdev, unsigned int nsid,
|
||||
struct nvme_id_ns *ns);
|
||||
|
||||
#if defined(CAS_NVME_FULL)
|
||||
|
||||
int cas_nvme_format_optimal(const char *device_path, int metadata_mode,
|
||||
int force);
|
||||
|
||||
#endif /* CAS_NVME_FULL */
|
||||
|
||||
#endif /* CAS_NVME_PARTIAL */
|
||||
|
||||
#endif /* UTILS_NVME_H_ */
|
769
modules/cas_cache/utils/utils_properties.c
Normal file
769
modules/cas_cache/utils/utils_properties.c
Normal file
@ -0,0 +1,769 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "cas_cache.h"
|
||||
|
||||
#define INTERNAL_CALL 0
|
||||
#define EXTERNAL_CALL 1
|
||||
|
||||
#define CAS_PROPERTIES_VERSION 101
|
||||
|
||||
#define VERSION_STR ".version"
|
||||
|
||||
/*
|
||||
* Difference between constant and non constant entry is store in LSB
|
||||
* e.g.:
|
||||
* cas_property_string in binary 0000 1010
|
||||
* cas_property_string_const in binary 0000 1011
|
||||
*/
|
||||
|
||||
#define CAS_PROP_UNCONST(type) (type & ~CAS_PROPERTIES_CONST)
|
||||
#define CAS_PROP_CHECK_CONST(type) (type & CAS_PROPERTIES_CONST)
|
||||
|
||||
enum cas_property_type {
|
||||
cas_property_string = 10,
|
||||
cas_property_string_const =
|
||||
(cas_property_string | CAS_PROPERTIES_CONST),
|
||||
cas_property_sint = 16,
|
||||
cas_property_sint_const = (cas_property_sint | CAS_PROPERTIES_CONST),
|
||||
cas_property_uint = 74,
|
||||
cas_property_uint_const = (cas_property_uint | CAS_PROPERTIES_CONST),
|
||||
};
|
||||
|
||||
struct cas_properties {
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct _cas_property {
|
||||
uint8_t type;
|
||||
char *key;
|
||||
struct list_head item;
|
||||
union {
|
||||
void *value;
|
||||
uint64_t value_uint;
|
||||
int64_t value_sint;
|
||||
};
|
||||
};
|
||||
|
||||
struct cas_properties *cas_properties_create(void)
|
||||
{
|
||||
struct cas_properties *props;
|
||||
int result;
|
||||
|
||||
props = kzalloc(sizeof(*props), GFP_KERNEL);
|
||||
if (!props)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
INIT_LIST_HEAD(&props->list);
|
||||
|
||||
result = cas_properties_add_uint(props, VERSION_STR,
|
||||
CAS_PROPERTIES_VERSION, CAS_PROPERTIES_CONST);
|
||||
if (result) {
|
||||
kfree(props);
|
||||
return ERR_PTR(result);
|
||||
}
|
||||
|
||||
result = cas_properties_add_uint(props, ".size", 0,
|
||||
CAS_PROPERTIES_NON_CONST);
|
||||
if (result) {
|
||||
kfree(props);
|
||||
return ERR_PTR(result);
|
||||
}
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
void cas_properties_destroy(struct cas_properties *props)
|
||||
{
|
||||
struct list_head *curr, *tmp;
|
||||
struct _cas_property *entry;
|
||||
|
||||
list_for_each_safe(curr, tmp, &props->list) {
|
||||
entry = list_entry(curr, struct _cas_property, item);
|
||||
list_del(curr);
|
||||
if (cas_property_string == CAS_PROP_UNCONST(entry->type))
|
||||
kfree(entry->value);
|
||||
kfree(entry->key);
|
||||
kfree(entry);
|
||||
}
|
||||
|
||||
kfree(props);
|
||||
}
|
||||
|
||||
static uint64_t _cas_prop_get_size(struct cas_properties *props)
|
||||
{
|
||||
struct list_head *curr;
|
||||
struct _cas_property *entry;
|
||||
uint64_t size_all = 0;
|
||||
|
||||
list_for_each(curr, &props->list) {
|
||||
entry = list_entry(curr, struct _cas_property, item);
|
||||
|
||||
size_all += cas_prop_strnlen(entry->key, MAX_STRING_SIZE) + 1;
|
||||
size_all += sizeof(entry->type);
|
||||
|
||||
switch (CAS_PROP_UNCONST(entry->type)) {
|
||||
case cas_property_string:
|
||||
size_all += cas_prop_strnlen(entry->value,
|
||||
MAX_STRING_SIZE) + 1;
|
||||
break;
|
||||
case cas_property_sint:
|
||||
size_all += sizeof(entry->value_sint);
|
||||
break;
|
||||
case cas_property_uint:
|
||||
size_all += sizeof(entry->value_uint);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return size_all;
|
||||
}
|
||||
|
||||
static int _cas_prop_serialize_string(char *buffer, const uint64_t size,
|
||||
uint64_t *offset, char *value)
|
||||
{
|
||||
uint64_t str_size = 0;
|
||||
|
||||
str_size = cas_prop_strnlen(value, MAX_STRING_SIZE) + 1;
|
||||
|
||||
if ((*offset + str_size) > size)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(buffer + *offset, value, str_size);
|
||||
*offset += str_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _cas_prop_parse_string(const char *buffer, const uint64_t size,
|
||||
uint64_t *offset, char **str)
|
||||
{
|
||||
char *tmp_str = NULL;
|
||||
uint64_t str_size = 0;
|
||||
|
||||
if (*offset >= size)
|
||||
return -ENOMEM;
|
||||
|
||||
str_size = cas_prop_strnlen(&buffer[*offset], size - *offset ) + 1;
|
||||
|
||||
if (str_size > size - *offset) {
|
||||
/* no null terminator at the end of buffer */
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tmp_str = kstrdup(&buffer[*offset], GFP_KERNEL);
|
||||
if (!tmp_str)
|
||||
return -ENOMEM;
|
||||
|
||||
*offset += str_size;
|
||||
*str = tmp_str;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _cas_prop_serialize_int(char *buffer, const uint64_t size,
|
||||
uint64_t *offset, uint64_t number)
|
||||
{
|
||||
int32_t i;
|
||||
|
||||
/*
|
||||
* To prevent issue connected with byte order we
|
||||
* serialize integer byte by byte.
|
||||
*/
|
||||
for (i = 0; i < sizeof(number); i++) {
|
||||
char byte = number & 0xFF;
|
||||
|
||||
if (*offset < size)
|
||||
buffer[*offset] = byte;
|
||||
else
|
||||
return -ENOMEM;
|
||||
|
||||
(*offset)++;
|
||||
number = number >> 8;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _cas_prop_serialize_uint(char *buffer, const uint64_t size,
|
||||
uint64_t *offset, uint64_t number)
|
||||
{
|
||||
return _cas_prop_serialize_int(buffer, size, offset, number);
|
||||
}
|
||||
|
||||
|
||||
static int _cas_prop_serialize_sint(char *buffer, const uint64_t size,
|
||||
uint64_t *offset, int64_t number)
|
||||
{
|
||||
return _cas_prop_serialize_int(buffer, size, offset, (uint64_t) number);
|
||||
|
||||
}
|
||||
|
||||
static int _cas_prop_parse_int(const char *buffer,
|
||||
const uint64_t size, uint64_t *offset, uint64_t *number)
|
||||
{
|
||||
int32_t i;
|
||||
uint64_t byte;
|
||||
|
||||
*number = 0;
|
||||
|
||||
/*
|
||||
* To prevent issue connected with byte order we
|
||||
* parse integer byte by byte.
|
||||
*/
|
||||
for (i = 0; i < sizeof(*number); i++) {
|
||||
if (*offset >= size)
|
||||
return -ENOMEM;
|
||||
|
||||
byte = buffer[*offset] & 0xFF;
|
||||
byte = byte << (i * 8);
|
||||
|
||||
*number |= byte;
|
||||
|
||||
(*offset)++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _cas_prop_parse_uint(const char *buffer,
|
||||
const uint64_t size, uint64_t *offset, uint64_t *number)
|
||||
{
|
||||
return _cas_prop_parse_int(buffer, size, offset, number);
|
||||
}
|
||||
|
||||
static int _cas_prop_parse_sint(const char *buffer,
|
||||
const uint64_t size, uint64_t *offset, int64_t *number)
|
||||
{
|
||||
return _cas_prop_parse_int(buffer, size, offset, (uint64_t *) number);
|
||||
}
|
||||
|
||||
static int _cas_prop_serialize(struct _cas_property *entry, void *buffer,
|
||||
const uint64_t size, uint64_t *offset)
|
||||
{
|
||||
uint64_t item_size = 0;
|
||||
void *item;
|
||||
int result = 0;
|
||||
|
||||
if (*offset > size)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Each entry is represented in buffer in order as below
|
||||
* (e.g. in case we have entry with integer) :
|
||||
* <----- entry ----->
|
||||
* <- key -><-type-><- integer ->
|
||||
* <- X bytes -><1 byte><- 8 byte ->
|
||||
* | | | |
|
||||
*/
|
||||
|
||||
/*
|
||||
* First step - serialize key
|
||||
*/
|
||||
|
||||
item_size = cas_prop_strnlen(entry->key, MAX_STRING_SIZE) + 1;
|
||||
item = entry->key;
|
||||
|
||||
if ((*offset + item_size) > size)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(buffer + *offset, item, item_size);
|
||||
*offset += item_size;
|
||||
|
||||
/*
|
||||
* Second step - serialize type
|
||||
*/
|
||||
|
||||
item_size = sizeof(entry->type);
|
||||
item = &entry->type;
|
||||
|
||||
if ((*offset + item_size) > size)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(buffer + *offset, item, item_size);
|
||||
*offset += item_size;
|
||||
|
||||
/*
|
||||
* Third step - serialize value
|
||||
*/
|
||||
|
||||
switch (CAS_PROP_UNCONST(entry->type)) {
|
||||
case cas_property_string:
|
||||
/* Serialize string */
|
||||
result = _cas_prop_serialize_string(buffer, size, offset,
|
||||
entry->value);
|
||||
break;
|
||||
case cas_property_sint:
|
||||
/* Serialize signed integer */
|
||||
result = _cas_prop_serialize_sint(buffer, size, offset,
|
||||
entry->value_uint);
|
||||
break;
|
||||
case cas_property_uint:
|
||||
/* Serialize unsigned integer */
|
||||
result = _cas_prop_serialize_uint(buffer, size, offset,
|
||||
entry->value_uint);
|
||||
break;
|
||||
default:
|
||||
result = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int cas_properties_serialize(struct cas_properties *props,
|
||||
struct casdsk_props_conf *caches_serialized_conf)
|
||||
{
|
||||
int result = 0;
|
||||
uint64_t offset = 0, size;
|
||||
uint16_t crc = 0;
|
||||
void *buffer;
|
||||
struct list_head *curr;
|
||||
struct _cas_property *entry;
|
||||
|
||||
size = _cas_prop_get_size(props);
|
||||
if (size == 0)
|
||||
return -EINVAL;
|
||||
|
||||
buffer = vzalloc(size);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Update first entry on list - size of buffer
|
||||
*/
|
||||
result = cas_properties_add_uint(props, ".size", size,
|
||||
CAS_PROPERTIES_CONST);
|
||||
if (result)
|
||||
goto error_after_buffer_allocation;
|
||||
|
||||
/*
|
||||
* Serialize each entry, one by one
|
||||
*/
|
||||
list_for_each(curr, &props->list) {
|
||||
entry = list_entry(curr, struct _cas_property, item);
|
||||
result = _cas_prop_serialize(entry, buffer, size, &offset);
|
||||
if (result)
|
||||
goto error_after_buffer_allocation;
|
||||
}
|
||||
|
||||
crc = crc16(0, buffer, size);
|
||||
|
||||
caches_serialized_conf->buffer = buffer;
|
||||
caches_serialized_conf->size = size;
|
||||
caches_serialized_conf->crc = crc;
|
||||
return result;
|
||||
|
||||
error_after_buffer_allocation:
|
||||
vfree(buffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
void cas_properties_print(struct cas_properties *props)
|
||||
{
|
||||
int result = 0;
|
||||
struct list_head *curr;
|
||||
struct _cas_property *entry;
|
||||
char *abc;
|
||||
|
||||
/*
|
||||
* Serialize each entry, one by one
|
||||
*/
|
||||
list_for_each(curr, &props->list) {
|
||||
entry = list_entry(curr, struct _cas_property, item);
|
||||
printk(KERN_DEBUG "[Upgrade] Key: %s", entry->key);
|
||||
switch (CAS_PROP_UNCONST(entry->type)) {
|
||||
case cas_property_string:
|
||||
printk(", string, ");
|
||||
abc = (char *)entry->value;
|
||||
printk("Value: %s ", abc);
|
||||
break;
|
||||
case cas_property_sint:
|
||||
break;
|
||||
case cas_property_uint:
|
||||
printk(", uint, ");
|
||||
printk("Value: %llu ", entry->value_uint);
|
||||
default:
|
||||
result = -EINVAL;
|
||||
break;
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int _cas_prop_parse_version(const char *buffer, uint64_t *offset,
|
||||
uint64_t *version, int trigger)
|
||||
{
|
||||
int result = 0;
|
||||
char *key = NULL;
|
||||
uint8_t type;
|
||||
|
||||
result = _cas_prop_parse_string(buffer, strlen(VERSION_STR) + 1,
|
||||
offset, &key);
|
||||
if (result)
|
||||
goto error_during_parse_key;
|
||||
|
||||
if (strcmp(VERSION_STR, key)) {
|
||||
result = -EINVAL;
|
||||
goto error_after_parse_key;
|
||||
}
|
||||
|
||||
type = buffer[*offset];
|
||||
if (cas_property_uint_const != type) {
|
||||
result = -EINVAL;
|
||||
goto error_after_parse_key;
|
||||
}
|
||||
*offset += sizeof(type);
|
||||
|
||||
result = _cas_prop_parse_uint(buffer,
|
||||
strlen(VERSION_STR) + 1 + sizeof(type) +
|
||||
sizeof(*version), offset, version);
|
||||
if (result)
|
||||
goto error_after_parse_key;
|
||||
|
||||
/*
|
||||
* In case that is external call
|
||||
* we don't need check version.
|
||||
*/
|
||||
if (trigger == INTERNAL_CALL && *version != CAS_PROPERTIES_VERSION) {
|
||||
printk(KERN_ERR "Version of interface using to parse is "
|
||||
"different than version used to serialize\n");
|
||||
result = -EPERM;
|
||||
}
|
||||
|
||||
error_after_parse_key:
|
||||
kfree(key);
|
||||
error_during_parse_key:
|
||||
return result;
|
||||
}
|
||||
|
||||
int cas_properites_parse_version(struct casdsk_props_conf *caches_serialized_conf,
|
||||
uint64_t *version)
|
||||
{
|
||||
uint64_t offset = 0;
|
||||
char *buffer = NULL;
|
||||
|
||||
buffer = (char *) caches_serialized_conf->buffer;
|
||||
if (!buffer)
|
||||
return -EINVAL;
|
||||
|
||||
return _cas_prop_parse_version(buffer, &offset, version, EXTERNAL_CALL);
|
||||
}
|
||||
|
||||
struct cas_properties *
|
||||
cas_properites_parse(struct casdsk_props_conf *caches_serialized_conf)
|
||||
{
|
||||
struct cas_properties *props;
|
||||
char *key = NULL, *value = NULL, *buffer = NULL;
|
||||
int result;
|
||||
uint8_t type;
|
||||
uint64_t uint_value, size = 0, offset = 0, version = 0;
|
||||
uint16_t crc;
|
||||
int64_t sint_value;
|
||||
bool constant = false;
|
||||
|
||||
props = cas_properties_create();
|
||||
if (IS_ERR(props))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (!caches_serialized_conf) {
|
||||
result = -EINVAL;
|
||||
goto error_after_props_allocation;
|
||||
}
|
||||
|
||||
buffer = (char *) caches_serialized_conf->buffer;
|
||||
if (!buffer) {
|
||||
result = -EINVAL;
|
||||
goto error_after_props_allocation;
|
||||
}
|
||||
|
||||
size = caches_serialized_conf->size;
|
||||
crc = crc16(0, buffer, size);
|
||||
if (crc != caches_serialized_conf->crc) {
|
||||
printk(KERN_ERR "Cache configuration corrupted");
|
||||
result = -EINVAL;
|
||||
goto error_after_props_allocation;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse first entry on list - version of interface used to
|
||||
* serialization
|
||||
*/
|
||||
result = _cas_prop_parse_version(buffer, &offset, &version,
|
||||
INTERNAL_CALL);
|
||||
if (result)
|
||||
goto error_after_props_allocation;
|
||||
|
||||
while (offset < size) {
|
||||
/*
|
||||
* Parse key of entry
|
||||
*/
|
||||
result = _cas_prop_parse_string(buffer, size, &offset, &key);
|
||||
if (result)
|
||||
goto error_after_props_allocation;
|
||||
|
||||
/*
|
||||
* Parse type of entry
|
||||
*/
|
||||
if (offset + sizeof(type) > size) {
|
||||
kfree(key);
|
||||
goto error_after_props_allocation;
|
||||
}
|
||||
|
||||
memcpy(&type, buffer + offset, sizeof(type));
|
||||
offset += sizeof(type);
|
||||
|
||||
constant = CAS_PROP_CHECK_CONST(type);
|
||||
type = CAS_PROP_UNCONST(type);
|
||||
|
||||
switch (type) {
|
||||
case cas_property_string:
|
||||
/* Parse string */
|
||||
result = _cas_prop_parse_string(buffer, size, &offset,
|
||||
&value);
|
||||
if (result)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Add new entry with string to CAS properties instance
|
||||
*/
|
||||
result |= cas_properties_add_string(props, key, value,
|
||||
constant);
|
||||
kfree(value);
|
||||
break;
|
||||
case cas_property_sint:
|
||||
/* Parse signed integer */
|
||||
result = _cas_prop_parse_sint(buffer, size, &offset,
|
||||
&sint_value);
|
||||
/* Add new entry with signed integer to CAS properties
|
||||
* instance
|
||||
*/
|
||||
result |= cas_properties_add_sint(props, key,
|
||||
sint_value, constant);
|
||||
break;
|
||||
case cas_property_uint:
|
||||
/* Parse unsigned integer */
|
||||
result = _cas_prop_parse_uint(buffer, size, &offset,
|
||||
&uint_value);
|
||||
/* Add new entry with unsigned integer to CAS properties
|
||||
* instance
|
||||
*/
|
||||
result |= cas_properties_add_uint(props, key,
|
||||
uint_value, constant);
|
||||
break;
|
||||
default:
|
||||
result = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* In case when we added new entry,
|
||||
* we not need hold key value longer.
|
||||
*/
|
||||
kfree(key);
|
||||
|
||||
if (result)
|
||||
goto error_after_props_allocation;
|
||||
}
|
||||
|
||||
return props;
|
||||
|
||||
error_after_props_allocation:
|
||||
cas_properties_destroy(props);
|
||||
return ERR_PTR(result);
|
||||
}
|
||||
|
||||
static struct _cas_property *_cas_prop_find(const struct cas_properties *props,
|
||||
const char *key)
|
||||
{
|
||||
struct list_head *curr;
|
||||
struct _cas_property *entry;
|
||||
|
||||
list_for_each(curr, &props->list) {
|
||||
entry = list_entry(curr, struct _cas_property, item);
|
||||
if (strncmp(key, entry->key, MAX_STRING_SIZE) == 0)
|
||||
return entry;
|
||||
}
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
|
||||
static struct _cas_property *_cas_prop_alloc_entry_key(const char *key)
|
||||
{
|
||||
struct _cas_property *entry;
|
||||
|
||||
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
entry->key = kstrdup(key, GFP_KERNEL);
|
||||
if (!entry->key) {
|
||||
kfree(entry);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&entry->item);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/*
|
||||
* ADD
|
||||
*/
|
||||
|
||||
int cas_properties_add_uint(struct cas_properties *props, const char *key,
|
||||
uint64_t value, bool constant)
|
||||
{
|
||||
struct _cas_property *entry;
|
||||
|
||||
/*
|
||||
* Looks for entry with same key,
|
||||
* if it is exist - update, if not - create new
|
||||
*/
|
||||
entry = _cas_prop_find(props, key);
|
||||
if (IS_ERR(entry)) {
|
||||
entry = _cas_prop_alloc_entry_key(key);
|
||||
if (IS_ERR(entry))
|
||||
return PTR_ERR(entry);
|
||||
list_add_tail(&entry->item, &props->list);
|
||||
} else if (cas_property_uint != entry->type) {
|
||||
/*
|
||||
* We can update only non constant entry,
|
||||
* so we need compare type only with non constant type.
|
||||
*/
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
entry->type = constant ? cas_property_uint_const : cas_property_uint;
|
||||
entry->value_uint = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cas_properties_add_sint(struct cas_properties *props, const char *key,
|
||||
int64_t value, bool constant)
|
||||
{
|
||||
struct _cas_property *entry;
|
||||
|
||||
/*
|
||||
* Looks for entry with same key,
|
||||
* if it is exist - update, if not - create new
|
||||
*/
|
||||
entry = _cas_prop_find(props, key);
|
||||
if (IS_ERR(entry)) {
|
||||
entry = _cas_prop_alloc_entry_key(key);
|
||||
if (IS_ERR(entry))
|
||||
return PTR_ERR(entry);
|
||||
list_add_tail(&entry->item, &props->list);
|
||||
} else if (cas_property_sint != entry->type) {
|
||||
/*
|
||||
* We can update only non constant entry,
|
||||
* so we need compare type only with non constant type.
|
||||
*/
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
entry->type = constant ? cas_property_sint_const : cas_property_sint;
|
||||
entry->value_sint = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cas_properties_add_string(struct cas_properties *props, const char *key,
|
||||
const char *value, bool constant)
|
||||
{
|
||||
struct _cas_property *entry;
|
||||
char *tmp_value = NULL;
|
||||
|
||||
tmp_value = kstrdup(value, GFP_KERNEL);
|
||||
if (!tmp_value)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Looks for entry with same key,
|
||||
* if it is exist - update, if not - create new
|
||||
*/
|
||||
entry = _cas_prop_find(props, key);
|
||||
if (IS_ERR(entry)) {
|
||||
entry = _cas_prop_alloc_entry_key(key);
|
||||
if (IS_ERR(entry)) {
|
||||
kfree(tmp_value);
|
||||
return PTR_ERR(entry);
|
||||
}
|
||||
list_add_tail(&entry->item, &props->list);
|
||||
} else {
|
||||
if (cas_property_string != entry->type) {
|
||||
/*
|
||||
* We can update only non constant entry,
|
||||
* so we need compare type only with non constant type.
|
||||
*/
|
||||
kfree(tmp_value);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
kfree(entry->value);
|
||||
}
|
||||
|
||||
entry->type = constant ? cas_property_string_const :
|
||||
cas_property_string;
|
||||
entry->value = tmp_value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* GET
|
||||
*/
|
||||
|
||||
int cas_properties_get_uint(struct cas_properties *props, const char *key,
|
||||
uint64_t *value)
|
||||
{
|
||||
struct _cas_property *entry;
|
||||
|
||||
entry = _cas_prop_find(props, key);
|
||||
if ((IS_ERR(entry) == 0) && (cas_property_uint ==
|
||||
CAS_PROP_UNCONST(entry->type))) {
|
||||
*value = entry->value_uint;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return IS_ERR(entry) ? PTR_ERR(entry) : -EINVAL;
|
||||
}
|
||||
|
||||
int cas_properties_get_sint(struct cas_properties *props, const char *key,
|
||||
int64_t *value)
|
||||
{
|
||||
struct _cas_property *entry;
|
||||
|
||||
entry = _cas_prop_find(props, key);
|
||||
if ((IS_ERR(entry) == 0) && (cas_property_sint ==
|
||||
CAS_PROP_UNCONST(entry->type))) {
|
||||
*value = entry->value_sint;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return IS_ERR(entry) ? PTR_ERR(entry) : -EINVAL;
|
||||
}
|
||||
|
||||
int cas_properties_get_string(struct cas_properties *props, const char *key,
|
||||
char *value, uint32_t size)
|
||||
{
|
||||
struct _cas_property *entry;
|
||||
|
||||
entry = _cas_prop_find(props, key);
|
||||
if ((IS_ERR(entry) == 0) && (cas_property_string ==
|
||||
CAS_PROP_UNCONST(entry->type))) {
|
||||
/* Check if size of destination memory is enough */
|
||||
if (size < cas_prop_strnlen(entry->value, MAX_STRING_SIZE) + 1)
|
||||
return -ENOMEM;
|
||||
|
||||
cas_prop_strncpy(value, size, entry->value,
|
||||
cas_prop_strnlen(entry->value, MAX_STRING_SIZE));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return IS_ERR(entry) ? PTR_ERR(entry) : -EINVAL;
|
||||
}
|
153
modules/cas_cache/utils/utils_properties.h
Normal file
153
modules/cas_cache/utils/utils_properties.h
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef UTILS_PROPERTIES_H_
|
||||
#define UTILS_PROPERTIES_H_
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#define cas_prop_strncpy(dest, dest_size, src, src_size) \
|
||||
strlcpy(dest, src, dest_size)
|
||||
#define cas_prop_strnlen(string, size) strnlen(string, size)
|
||||
#else
|
||||
#define cas_prop_strncpy(dest, dest_size, src, src_size) \
|
||||
strncpy(dest, src, src_size)
|
||||
#define cas_prop_strnlen(string, size) strlen(string)
|
||||
#endif
|
||||
|
||||
#include "../../cas_disk/cas_disk.h"
|
||||
|
||||
#define MAX_STRING_SIZE 4095
|
||||
|
||||
#define CAS_PROPERTIES_NON_CONST false
|
||||
#define CAS_PROPERTIES_CONST true
|
||||
|
||||
/**
|
||||
* @file utils_properties.h
|
||||
* @brief CAS cache interface for collect and serialization CAS properties
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Handler for instance of CAS properties
|
||||
*/
|
||||
struct cas_properties;
|
||||
|
||||
/**
|
||||
* @brief Initialize instance of CAS properties
|
||||
*
|
||||
* @return Handler to instance of interface
|
||||
*/
|
||||
struct cas_properties *cas_properties_create(void);
|
||||
|
||||
/**
|
||||
* @brief De-initialize instance of CAS properties
|
||||
*
|
||||
* @param props Handler to instance to de-initialize
|
||||
*/
|
||||
void cas_properties_destroy(struct cas_properties *props);
|
||||
|
||||
/**
|
||||
* @brief Serialize given CAS properties instance to continuous buffer
|
||||
*
|
||||
* @param props instance of CAS properties
|
||||
* @param idisk conf instance of CAS properties
|
||||
* @return result of serialize CAS properties
|
||||
*/
|
||||
int cas_properties_serialize(struct cas_properties *props,
|
||||
struct casdsk_props_conf *caches_serialized_conf);
|
||||
|
||||
/**
|
||||
* @brief Parse of first entry given continuous buffer to get version of
|
||||
* interface which been used to serialize
|
||||
*
|
||||
* @param buffer pointer to continuous buffer with serialized CAS properties
|
||||
* @param version pointer to memory where we will put version
|
||||
* @return result of getting version, 0 success
|
||||
*/
|
||||
int cas_properites_parse_version(struct casdsk_props_conf *caches_serialized_conf,
|
||||
uint64_t *version);
|
||||
|
||||
/**
|
||||
* @brief Parse of given continuous buffer to CAS properties instance
|
||||
*
|
||||
* @param buffer pointer to continuous buffer with serialized CAS properties
|
||||
* @return handler to CAS properties instance
|
||||
*/
|
||||
struct cas_properties *
|
||||
cas_properites_parse(struct casdsk_props_conf *caches_serialized_conf);
|
||||
|
||||
/**
|
||||
* @brief Add unsigned integer to CAS properties instance
|
||||
*
|
||||
* @param props CAS properties instance to add variable
|
||||
* @param key key paired with variable
|
||||
* @param value value of variable
|
||||
* @param private if true value cannot be updated
|
||||
* @return result of adding 0 success
|
||||
*/
|
||||
int cas_properties_add_uint(struct cas_properties *props, const char *key,
|
||||
uint64_t value, bool private);
|
||||
|
||||
/**
|
||||
* @brief Add signed integer to CAS properties instance
|
||||
*
|
||||
* @param props CAS properties instance to add variable
|
||||
* @param key key paired with variable
|
||||
* @param value value of variable
|
||||
* @param private if true value cannot be updated
|
||||
* @return result of adding 0 success
|
||||
*/
|
||||
int cas_properties_add_sint(struct cas_properties *props, const char *key,
|
||||
int64_t value, bool private);
|
||||
|
||||
/**
|
||||
* @brief Add string to CAS properties instance
|
||||
*
|
||||
* @param props CAS properties instance to add variable
|
||||
* @param key key paired with variable
|
||||
* @param value value of variable
|
||||
* @param private if true value cannot be updated
|
||||
* @return result of adding 0 success
|
||||
*/
|
||||
int cas_properties_add_string(struct cas_properties *props, const char *key,
|
||||
const char *value, bool private);
|
||||
|
||||
/**
|
||||
* @brief Get unsigned integer to CAS properties instance
|
||||
*
|
||||
* @param props CAS properties instance to add variable
|
||||
* @param key key paired with variable
|
||||
* @param value pointer to memory where we will put value
|
||||
* @return result of getting 0 success
|
||||
*/
|
||||
int cas_properties_get_uint(struct cas_properties *props, const char *key,
|
||||
uint64_t *value);
|
||||
|
||||
/**
|
||||
* @brief Get signed integer to CAS properties instance
|
||||
*
|
||||
* @param props CAS properties instance to add variable
|
||||
* @param key key paired with variable
|
||||
* @param value pointer to memory where we will put value
|
||||
* @return result of getting 0 success
|
||||
*/
|
||||
int cas_properties_get_sint(struct cas_properties *props, const char *key,
|
||||
int64_t *value);
|
||||
|
||||
/**
|
||||
* @brief Get string integer to CAS properties instance
|
||||
*
|
||||
* @param props CAS properties instance to add variable
|
||||
* @param key key paired with variable
|
||||
* @param value pointer to memory where we will put value
|
||||
* @param size size of destination memory
|
||||
* @return result of getting 0 success, 1 error, 2 not enough space
|
||||
* in destination
|
||||
*/
|
||||
int cas_properties_get_string(struct cas_properties *props, const char *key,
|
||||
char *value, uint32_t size);
|
||||
|
||||
|
||||
void cas_properties_print(struct cas_properties *props);
|
||||
#endif /* UTILS_PROPERTIES_H_ */
|
262
modules/cas_cache/utils/utils_rpool.c
Normal file
262
modules/cas_cache/utils/utils_rpool.c
Normal file
@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "ocf/ocf.h"
|
||||
#include "utils_rpool.h"
|
||||
#include "ocf_env.h"
|
||||
#include "../cas_cache.h"
|
||||
|
||||
#define CAS_UTILS_RPOOL_DEBUG 0
|
||||
#if 1 == CAS_UTILS_RPOOL_DEBUG
|
||||
#define CAS_DEBUG_TRACE() \
|
||||
printk(KERN_INFO "[Utils][RPOOL] %s\n", __func__)
|
||||
|
||||
#define CAS_DEBUG_MSG(msg) \
|
||||
printk(KERN_INFO "[Utils][RPOOL] %s - %s\n", __func__, msg)
|
||||
|
||||
#define CAS_DEBUG_PARAM(format, ...) \
|
||||
printk(KERN_INFO "[Utils][RPOOL] %s - "format"\n", \
|
||||
__func__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define CAS_DEBUG_TRACE()
|
||||
#define CAS_DEBUG_MSG(msg)
|
||||
#define CAS_DEBUG_PARAM(format, ...)
|
||||
#endif
|
||||
|
||||
struct _cas_reserve_pool_per_cpu {
|
||||
spinlock_t lock;
|
||||
struct list_head list;
|
||||
atomic_t count;
|
||||
};
|
||||
|
||||
struct cas_reserve_pool {
|
||||
uint32_t limit;
|
||||
char *name;
|
||||
uint32_t entry_size;
|
||||
struct _cas_reserve_pool_per_cpu *rpools;
|
||||
};
|
||||
|
||||
struct _cas_rpool_pre_alloc_info {
|
||||
struct work_struct ws;
|
||||
struct cas_reserve_pool *rpool_master;
|
||||
cas_rpool_new rpool_new;
|
||||
void *allocator_ctx;
|
||||
struct completion cmpl;
|
||||
int error;
|
||||
};
|
||||
|
||||
#define RPOOL_ITEM_TO_ENTRY(rpool, item) \
|
||||
(void *)((unsigned long)item + sizeof(struct list_head) \
|
||||
- rpool->entry_size)
|
||||
|
||||
#define RPOOL_ENTRY_TO_ITEM(rpool, entry) \
|
||||
(struct list_head *)((unsigned long)entry + rpool->entry_size \
|
||||
- sizeof(struct list_head))
|
||||
|
||||
void _cas_rpool_pre_alloc_do(struct work_struct *ws)
|
||||
{
|
||||
struct _cas_rpool_pre_alloc_info *info =
|
||||
container_of(ws, struct _cas_rpool_pre_alloc_info, ws);
|
||||
struct cas_reserve_pool *rpool_master = info->rpool_master;
|
||||
struct _cas_reserve_pool_per_cpu *current_rpool;
|
||||
struct list_head *item;
|
||||
void *entry;
|
||||
int i, cpu;
|
||||
|
||||
CAS_DEBUG_TRACE();
|
||||
|
||||
cpu = smp_processor_id();
|
||||
current_rpool = &rpool_master->rpools[cpu];
|
||||
|
||||
for (i = 0; i < rpool_master->limit; i++) {
|
||||
entry = info->rpool_new(info->allocator_ctx, cpu);
|
||||
if (!entry) {
|
||||
info->error = -ENOMEM;
|
||||
complete(&info->cmpl);
|
||||
return;
|
||||
}
|
||||
item = RPOOL_ENTRY_TO_ITEM(rpool_master, entry);
|
||||
list_add_tail(item, ¤t_rpool->list);
|
||||
atomic_inc(¤t_rpool->count);
|
||||
}
|
||||
|
||||
CAS_DEBUG_PARAM("Added [%d] pre allocated items to reserve poll [%s]"
|
||||
" for cpu %d", atomic_read(¤t_rpool->count),
|
||||
rpool_master->name, cpu);
|
||||
|
||||
complete(&info->cmpl);
|
||||
}
|
||||
|
||||
|
||||
int _cas_rpool_pre_alloc_schedule(int cpu,
|
||||
struct _cas_rpool_pre_alloc_info *info)
|
||||
{
|
||||
init_completion(&info->cmpl);
|
||||
INIT_WORK(&info->ws, _cas_rpool_pre_alloc_do);
|
||||
schedule_work_on(cpu, &info->ws);
|
||||
schedule();
|
||||
|
||||
wait_for_completion(&info->cmpl);
|
||||
return info->error;
|
||||
}
|
||||
|
||||
void cas_rpool_destroy(struct cas_reserve_pool *rpool_master,
|
||||
cas_rpool_del rpool_del, void *allocator_ctx)
|
||||
{
|
||||
int i, cpu_no = num_online_cpus();
|
||||
struct _cas_reserve_pool_per_cpu *current_rpool = NULL;
|
||||
struct list_head *item = NULL, *next = NULL;
|
||||
void *entry;
|
||||
|
||||
CAS_DEBUG_TRACE();
|
||||
|
||||
if (!rpool_master)
|
||||
return;
|
||||
|
||||
if (!rpool_master->rpools) {
|
||||
kfree(rpool_master);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < cpu_no; i++) {
|
||||
current_rpool = &rpool_master->rpools[i];
|
||||
|
||||
CAS_DEBUG_PARAM("Destroyed reserve poll [%s] for cpu %d",
|
||||
rpool_master->name, i);
|
||||
|
||||
if (!atomic_read(¤t_rpool->count))
|
||||
continue;
|
||||
|
||||
list_for_each_safe(item, next, ¤t_rpool->list) {
|
||||
entry = RPOOL_ITEM_TO_ENTRY(rpool_master, item);
|
||||
list_del(item);
|
||||
rpool_del(allocator_ctx, entry);
|
||||
atomic_dec(¤t_rpool->count);
|
||||
}
|
||||
|
||||
if (atomic_read(¤t_rpool->count)) {
|
||||
printk(KERN_CRIT "Not all object from reserve poll"
|
||||
"[%s] deallocated\n", rpool_master->name);
|
||||
WARN(true, OCF_PREFIX_SHORT" Cleanup problem\n");
|
||||
}
|
||||
}
|
||||
|
||||
kfree(rpool_master->rpools);
|
||||
kfree(rpool_master);
|
||||
}
|
||||
|
||||
struct cas_reserve_pool *cas_rpool_create(uint32_t limit, char *name,
|
||||
uint32_t entry_size, cas_rpool_new rpool_new,
|
||||
cas_rpool_del rpool_del, void *allocator_ctx)
|
||||
{
|
||||
int i, cpu_no = num_online_cpus();
|
||||
struct cas_reserve_pool *rpool_master = NULL;
|
||||
struct _cas_reserve_pool_per_cpu *current_rpool = NULL;
|
||||
struct _cas_rpool_pre_alloc_info info;
|
||||
|
||||
CAS_DEBUG_TRACE();
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
|
||||
rpool_master = kzalloc(sizeof(*rpool_master), GFP_KERNEL);
|
||||
if (!rpool_master)
|
||||
goto error;
|
||||
|
||||
rpool_master->rpools = kzalloc(sizeof(*rpool_master->rpools) * cpu_no,
|
||||
GFP_KERNEL);
|
||||
if (!rpool_master->rpools)
|
||||
goto error;
|
||||
|
||||
rpool_master->limit = limit;
|
||||
rpool_master->name = name;
|
||||
rpool_master->entry_size = entry_size;
|
||||
|
||||
info.rpool_master = rpool_master;
|
||||
info.rpool_new = rpool_new;
|
||||
info.allocator_ctx = allocator_ctx;
|
||||
|
||||
for (i = 0; i < cpu_no; i++) {
|
||||
current_rpool = &rpool_master->rpools[i];
|
||||
spin_lock_init(¤t_rpool->lock);
|
||||
INIT_LIST_HEAD(¤t_rpool->list);
|
||||
|
||||
if (_cas_rpool_pre_alloc_schedule(i, &info))
|
||||
goto error;
|
||||
|
||||
CAS_DEBUG_PARAM("Created reserve poll [%s] for cpu %d",
|
||||
rpool_master->name, i);
|
||||
}
|
||||
|
||||
return rpool_master;
|
||||
error:
|
||||
|
||||
cas_rpool_destroy(rpool_master, rpool_del, allocator_ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define LIST_FIRST_ITEM(head) head.next
|
||||
|
||||
void *cas_rpool_try_get(struct cas_reserve_pool *rpool_master, int *cpu)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct _cas_reserve_pool_per_cpu *current_rpool = NULL;
|
||||
struct list_head *item = NULL;
|
||||
void *entry = NULL;
|
||||
|
||||
CAS_DEBUG_TRACE();
|
||||
|
||||
*cpu = smp_processor_id();
|
||||
current_rpool = &rpool_master->rpools[*cpu];
|
||||
|
||||
spin_lock_irqsave(¤t_rpool->lock, flags);
|
||||
|
||||
if (!list_empty(¤t_rpool->list)) {
|
||||
item = LIST_FIRST_ITEM(current_rpool->list);
|
||||
entry = RPOOL_ITEM_TO_ENTRY(rpool_master, item);
|
||||
list_del(item);
|
||||
atomic_dec(¤t_rpool->count);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(¤t_rpool->lock, flags);
|
||||
|
||||
CAS_DEBUG_PARAM("[%s]Removed item from reserve pool [%s] for cpu [%d], "
|
||||
"items in pool %d", rpool_master->name,
|
||||
item == NULL ? "SKIPPED" : "OK", *cpu,
|
||||
atomic_read(¤t_rpool->count));
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
int cas_rpool_try_put(struct cas_reserve_pool *rpool_master, void *entry, int cpu)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
struct _cas_reserve_pool_per_cpu *current_rpool = NULL;
|
||||
struct list_head *item;
|
||||
|
||||
CAS_DEBUG_TRACE();
|
||||
|
||||
current_rpool = &rpool_master->rpools[cpu];
|
||||
|
||||
spin_lock_irqsave(¤t_rpool->lock, flags);
|
||||
|
||||
if (atomic_read(¤t_rpool->count) >= rpool_master->limit) {
|
||||
ret = 1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
item = RPOOL_ENTRY_TO_ITEM(rpool_master, entry);
|
||||
list_add_tail(item, ¤t_rpool->list);
|
||||
|
||||
atomic_inc(¤t_rpool->count);
|
||||
|
||||
error:
|
||||
CAS_DEBUG_PARAM("[%s]Added item to reserve pool [%s] for cpu [%d], "
|
||||
"items in pool %d", rpool_master->name,
|
||||
ret == 1 ? "SKIPPED" : "OK", cpu,
|
||||
atomic_read(¤t_rpool->count));
|
||||
spin_unlock_irqrestore(¤t_rpool->lock, flags);
|
||||
return ret;
|
||||
}
|
28
modules/cas_cache/utils/utils_rpool.h
Normal file
28
modules/cas_cache/utils/utils_rpool.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef __CAS_RPOOL_H__
|
||||
#define __CAS_RPOOL_H__
|
||||
|
||||
#define CAS_RPOOL_MIN_SIZE_ITEM sizeof(struct list_head)
|
||||
|
||||
struct cas_reserve_pool;
|
||||
|
||||
typedef void (*cas_rpool_del)(void *allocator_ctx, void *item);
|
||||
typedef void *(*cas_rpool_new)(void *allocator_ctx, int cpu);
|
||||
|
||||
struct cas_reserve_pool *cas_rpool_create(uint32_t limit, char *name,
|
||||
uint32_t item_size, cas_rpool_new rpool_new,
|
||||
cas_rpool_del rpool_del, void *allocator_ctx);
|
||||
|
||||
void cas_rpool_destroy(struct cas_reserve_pool *rpool,
|
||||
cas_rpool_del rpool_del, void *allocator_ctx);
|
||||
|
||||
void *cas_rpool_try_get(struct cas_reserve_pool *rpool, int *cpu);
|
||||
|
||||
int cas_rpool_try_put(struct cas_reserve_pool *rpool, void *item, int cpu);
|
||||
|
||||
#endif /* __CAS_RPOOL_H__ */
|
||||
|
53
modules/cas_cache/volume/obj_blk.h
Normal file
53
modules/cas_cache/volume/obj_blk.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef __OBJ_BLK_H__
|
||||
#define __OBJ_BLK_H__
|
||||
|
||||
#include "vol_atomic_dev_bottom.h"
|
||||
#include "vol_block_dev_bottom.h"
|
||||
#include "vol_block_dev_top.h"
|
||||
|
||||
struct casdsk_disk;
|
||||
|
||||
struct bd_object {
|
||||
struct casdsk_disk *dsk;
|
||||
struct block_device *btm_bd;
|
||||
/**
|
||||
* This denotes state of volatile write cache of the device.
|
||||
* This is set to true when:
|
||||
* - opening the device
|
||||
* - when writing to a device without FUA/FLUSH flags
|
||||
* This is set to false when:
|
||||
* - FLUSH request is completed on device.
|
||||
* When it is false
|
||||
* - FLUSH requests from upper layer are NOT passed to the device.
|
||||
*/
|
||||
atomic_t potentially_dirty;
|
||||
|
||||
uint32_t expobj_valid : 1;
|
||||
/*!< Bit indicates that exported object was created */
|
||||
|
||||
uint32_t expobj_locked : 1;
|
||||
/*!< Non zero value indicates data exported object is locked */
|
||||
|
||||
uint32_t opened_by_bdev : 1;
|
||||
/*!< Opened by supplying bdev manually */
|
||||
|
||||
struct atomic_dev_params atomic_params;
|
||||
|
||||
atomic64_t pending_rqs;
|
||||
/*!< This fields describes in flight IO requests */
|
||||
|
||||
struct workqueue_struct *workqueue;
|
||||
/*< Workqueue for internally trigerred I/O */
|
||||
};
|
||||
|
||||
static inline struct bd_object *bd_object(ocf_volume_t vol)
|
||||
{
|
||||
return ocf_volume_get_priv(vol);
|
||||
}
|
||||
|
||||
#endif /* __OBJ_BLK_H__ */
|
1199
modules/cas_cache/volume/vol_atomic_dev_bottom.c
Normal file
1199
modules/cas_cache/volume/vol_atomic_dev_bottom.c
Normal file
File diff suppressed because it is too large
Load Diff
1217
modules/cas_cache/volume/vol_atomic_dev_bottom.c.orig
Normal file
1217
modules/cas_cache/volume/vol_atomic_dev_bottom.c.orig
Normal file
File diff suppressed because it is too large
Load Diff
31
modules/cas_cache/volume/vol_atomic_dev_bottom.h
Normal file
31
modules/cas_cache/volume/vol_atomic_dev_bottom.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef __VOL_ATOMIC_DEV_BOTTOM_H__
|
||||
#define __VOL_ATOMIC_DEV_BOTTOM_H__
|
||||
|
||||
#include "../cas_cache.h"
|
||||
|
||||
enum atomic_metadata_mode {
|
||||
ATOMIC_METADATA_MODE_ELBA,
|
||||
ATOMIC_METADATA_MODE_SEPBUF,
|
||||
ATOMIC_METADATA_MODE_NONE,
|
||||
};
|
||||
|
||||
struct atomic_dev_params {
|
||||
unsigned int nsid;
|
||||
uint64_t size;
|
||||
enum atomic_metadata_mode metadata_mode;
|
||||
unsigned is_mode_optimal : 1;
|
||||
|
||||
/* IMPORTANT: If this field is 0, the other fields are invalid! */
|
||||
unsigned is_atomic_capable : 1;
|
||||
};
|
||||
|
||||
int atomic_dev_init(void);
|
||||
|
||||
void atomic_dev_deinit(void);
|
||||
|
||||
#endif /* __VOL_ATOMIC_DEV_BOTTOM_H__ */
|
470
modules/cas_cache/volume/vol_blk_utils.c
Normal file
470
modules/cas_cache/volume/vol_blk_utils.c
Normal file
@ -0,0 +1,470 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#include "vol_blk_utils.h"
|
||||
|
||||
static void cas_io_iter_advanced(struct bio_vec_iter *iter, uint32_t bytes)
|
||||
{
|
||||
BUG_ON(bytes > iter->len);
|
||||
|
||||
iter->len -= bytes;
|
||||
iter->offset += bytes;
|
||||
|
||||
if (iter->len) {
|
||||
/* Still in this item, bytes to be processed */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Move to next item in data vector */
|
||||
iter->idx++;
|
||||
if (iter->idx < iter->vec_size) {
|
||||
iter->ivec = &iter->vec[iter->idx];
|
||||
iter->len = iter->ivec->bv_len;
|
||||
iter->offset = iter->ivec->bv_offset;
|
||||
} else {
|
||||
iter->ivec = NULL;
|
||||
iter->len = 0;
|
||||
iter->offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t cas_io_iter_cpy(struct bio_vec_iter *dst, struct bio_vec_iter *src,
|
||||
uint32_t bytes)
|
||||
{
|
||||
uint32_t to_copy, written = 0;
|
||||
void *adst, *asrc;
|
||||
|
||||
if (dst->idx >= dst->vec_size)
|
||||
return 0;
|
||||
|
||||
BUG_ON(dst->offset + dst->len > PAGE_SIZE);
|
||||
|
||||
if (src->idx >= src->vec_size)
|
||||
return 0;
|
||||
|
||||
BUG_ON(src->offset + src->len > PAGE_SIZE);
|
||||
|
||||
while (bytes) {
|
||||
to_copy = min(dst->len, src->len);
|
||||
to_copy = min(to_copy, bytes);
|
||||
if (to_copy == 0) {
|
||||
/* No more bytes for coping */
|
||||
break;
|
||||
}
|
||||
|
||||
adst = page_address(dst->ivec->bv_page) + dst->offset;
|
||||
asrc = page_address(src->ivec->bv_page) + src->offset;
|
||||
|
||||
memcpy(adst, asrc, to_copy);
|
||||
|
||||
bytes -= to_copy;
|
||||
written += to_copy;
|
||||
|
||||
cas_io_iter_advanced(dst, to_copy);
|
||||
cas_io_iter_advanced(src, to_copy);
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
uint32_t cas_io_iter_cpy_from_data(struct bio_vec_iter *dst,
|
||||
const void *src, uint32_t bytes)
|
||||
{
|
||||
uint32_t to_copy, written = 0;
|
||||
void *adst;
|
||||
const void *asrc;
|
||||
|
||||
if (dst->idx >= dst->vec_size)
|
||||
return 0;
|
||||
|
||||
BUG_ON(dst->offset + dst->len > PAGE_SIZE);
|
||||
|
||||
while (bytes) {
|
||||
to_copy = min(dst->len, bytes);
|
||||
if (to_copy == 0) {
|
||||
/* No more bytes for coping */
|
||||
break;
|
||||
}
|
||||
|
||||
adst = page_address(dst->ivec->bv_page) + dst->offset;
|
||||
asrc = src + written;
|
||||
|
||||
memcpy(adst, asrc, to_copy);
|
||||
|
||||
bytes -= to_copy;
|
||||
written += to_copy;
|
||||
|
||||
cas_io_iter_advanced(dst, to_copy);
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
uint32_t cas_io_iter_cpy_to_data(void *dst, struct bio_vec_iter *src,
|
||||
uint32_t bytes)
|
||||
{
|
||||
uint32_t to_copy, written = 0;
|
||||
void *adst, *asrc;
|
||||
|
||||
BUG_ON(dst == NULL);
|
||||
|
||||
if (src->idx >= src->vec_size)
|
||||
return 0;
|
||||
|
||||
BUG_ON(src->offset + src->len > PAGE_SIZE);
|
||||
|
||||
while (bytes) {
|
||||
to_copy = min(bytes, src->len);
|
||||
if (to_copy == 0) {
|
||||
/* No more bytes for coping */
|
||||
break;
|
||||
}
|
||||
|
||||
adst = dst + written;
|
||||
asrc = page_address(src->ivec->bv_page) + src->offset;
|
||||
|
||||
memcpy(adst, asrc, to_copy);
|
||||
|
||||
bytes -= to_copy;
|
||||
written += to_copy;
|
||||
|
||||
cas_io_iter_advanced(src, to_copy);
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
uint32_t cas_io_iter_move(struct bio_vec_iter *iter, uint32_t bytes)
|
||||
{
|
||||
uint32_t to_move, moved = 0;
|
||||
|
||||
if (iter->idx >= iter->vec_size)
|
||||
return 0;
|
||||
|
||||
BUG_ON(iter->offset + iter->len > PAGE_SIZE);
|
||||
|
||||
while (bytes) {
|
||||
to_move = min(iter->len, bytes);
|
||||
if (to_move == 0) {
|
||||
/* No more bytes for coping */
|
||||
break;
|
||||
}
|
||||
|
||||
bytes -= to_move;
|
||||
moved += to_move;
|
||||
|
||||
cas_io_iter_advanced(iter, to_move);
|
||||
}
|
||||
|
||||
return moved;
|
||||
}
|
||||
|
||||
uint32_t cas_io_iter_zero(struct bio_vec_iter *dst, uint32_t bytes)
|
||||
{
|
||||
uint32_t to_fill, zeroed = 0;
|
||||
void *adst;
|
||||
|
||||
if (dst->idx >= dst->vec_size)
|
||||
return 0;
|
||||
|
||||
BUG_ON(dst->offset + dst->len > PAGE_SIZE);
|
||||
|
||||
while (bytes) {
|
||||
to_fill = min(dst->len, (typeof(dst->len))PAGE_SIZE);
|
||||
if (to_fill == 0) {
|
||||
/* No more bytes for coping */
|
||||
break;
|
||||
}
|
||||
|
||||
adst = page_address(dst->ivec->bv_page) + dst->offset;
|
||||
|
||||
memset(adst, 0, to_fill);
|
||||
|
||||
bytes -= to_fill;
|
||||
zeroed += to_fill;
|
||||
|
||||
cas_io_iter_advanced(dst, to_fill);
|
||||
}
|
||||
|
||||
return zeroed;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
int cas_blk_io_set_data(struct ocf_io *io,
|
||||
ctx_data_t *ctx_data, uint32_t offset)
|
||||
{
|
||||
struct blkio *blkio = cas_io_to_blkio(io);
|
||||
struct blk_data *data = ctx_data;
|
||||
|
||||
/* Set BIO vector (IO data) and initialize iterator */
|
||||
blkio->data = data;
|
||||
if (blkio->data) {
|
||||
cas_io_iter_init(&blkio->iter, blkio->data->vec,
|
||||
blkio->data->size);
|
||||
|
||||
/* Move into specified offset in BIO vector iterator */
|
||||
if (offset != cas_io_iter_move(&blkio->iter, offset)) {
|
||||
/* TODO Log message */
|
||||
blkio->error = -ENOBUFS;
|
||||
return -ENOBUFS;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
ctx_data_t *cas_blk_io_get_data(struct ocf_io *io)
|
||||
{
|
||||
struct blkio *blkio = cas_io_to_blkio(io);
|
||||
|
||||
return blkio->data;
|
||||
}
|
||||
|
||||
#if defined(CAS_NVME_PARTIAL)
|
||||
|
||||
#include "utils/utils_nvme.h"
|
||||
|
||||
int cas_blk_identify_type_by_bdev(struct block_device *bdev,
|
||||
uint8_t *type, struct atomic_dev_params *atomic_params)
|
||||
{
|
||||
struct nvme_id_ns *ns;
|
||||
unsigned int nsid, selected, ms, ds, pi, elba, sbsupp;
|
||||
long long int ret = 0;
|
||||
struct atomic_dev_params atomic_params_int = {0};
|
||||
|
||||
ns = kmalloc(sizeof(*ns), GFP_KERNEL);
|
||||
if (!ns)
|
||||
return -OCF_ERR_NO_MEM;
|
||||
|
||||
ret = cas_nvme_get_nsid(bdev, &nsid);
|
||||
if (ret < 0) {
|
||||
/*
|
||||
* We cannot obtain NSID which means we are not dealing with
|
||||
* NVMe device
|
||||
*/
|
||||
goto out1;
|
||||
}
|
||||
|
||||
ret = cas_nvme_identify_ns(bdev, nsid, ns);
|
||||
if (ret < 0) {
|
||||
/*
|
||||
* We cannot obtain ns structure which means we ARE dealing with
|
||||
* NVMe device but can not recognize format so let's treat that
|
||||
* device as block device
|
||||
*/
|
||||
goto out1;
|
||||
}
|
||||
|
||||
selected = ns->flbas & 0xf;
|
||||
ms = ns->lbaf[selected].ms;
|
||||
ds = ns->lbaf[selected].ds;
|
||||
pi = ns->dps & 0x7;
|
||||
elba = !!(ns->flbas & (1<<4));
|
||||
sbsupp = !!(ns->mc & (1<<1));
|
||||
|
||||
atomic_params_int.is_atomic_capable = 1;
|
||||
atomic_params_int.nsid = nsid;
|
||||
atomic_params_int.size = (ns->nsze << (ds - 9)) * SECTOR_SIZE;
|
||||
|
||||
if (pi != 0) {
|
||||
/* We don't support formats which have
|
||||
* enable Protection Information feature.
|
||||
*/
|
||||
ret = -KCAS_ERR_NVME_BAD_FORMAT;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
switch (ms) {
|
||||
case 0:
|
||||
/* NVMe metadata features disabled, so we handle it as
|
||||
* regular block device
|
||||
*/
|
||||
|
||||
if (ds != 9 && ds != 12) {
|
||||
ret = -KCAS_ERR_NVME_BAD_FORMAT;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
*type = BLOCK_DEVICE_VOLUME;
|
||||
atomic_params_int.metadata_mode = ATOMIC_METADATA_MODE_NONE;
|
||||
|
||||
#if !defined(CAS_NVME_FULL)
|
||||
/*
|
||||
* Only partial support user can't using
|
||||
* device in atomic mode, so mode is optimal
|
||||
*/
|
||||
atomic_params_int.is_mode_optimal = 1;
|
||||
break;
|
||||
#else
|
||||
if (bdev == bdev->bd_contains) {
|
||||
/*
|
||||
* Entire device - format isn't optimal
|
||||
*/
|
||||
atomic_params_int.is_mode_optimal = 0;
|
||||
} else {
|
||||
/*
|
||||
* Partition - format is optimal, user can't using
|
||||
* partitions in atomic mode
|
||||
*/
|
||||
atomic_params_int.is_mode_optimal = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 8:
|
||||
/* For atomic writes we support only metadata size 8B and
|
||||
* data size 512B
|
||||
*/
|
||||
|
||||
if (ds != 9) {
|
||||
ret = -KCAS_ERR_NVME_BAD_FORMAT;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
*type = ATOMIC_DEVICE_VOLUME;
|
||||
atomic_params_int.metadata_mode = elba ?
|
||||
ATOMIC_METADATA_MODE_ELBA :
|
||||
ATOMIC_METADATA_MODE_SEPBUF;
|
||||
atomic_params_int.is_mode_optimal = sbsupp ? !elba : 1;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
ret = -KCAS_ERR_NVME_BAD_FORMAT;
|
||||
}
|
||||
|
||||
if (atomic_params)
|
||||
*atomic_params = atomic_params_int;
|
||||
|
||||
goto out2;
|
||||
out1:
|
||||
*type = BLOCK_DEVICE_VOLUME;
|
||||
ret = 0;
|
||||
out2:
|
||||
kfree(ns);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int _cas_detect_blk_type(const char *path, uint8_t *type,
|
||||
struct atomic_dev_params *atomic_params)
|
||||
{
|
||||
int ret;
|
||||
struct block_device *bdev;
|
||||
char holder[] = "CAS DETECT\n";
|
||||
|
||||
bdev = OPEN_BDEV_EXCLUSIVE(path, FMODE_READ, holder);
|
||||
if (IS_ERR(bdev))
|
||||
return -OCF_ERR_NOT_OPEN_EXC;
|
||||
|
||||
ret = cas_blk_identify_type_by_bdev(bdev, type, atomic_params);
|
||||
CLOSE_BDEV_EXCLUSIVE(bdev, FMODE_READ);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int _cas_detect_blk_type(const char *path, uint8_t *type,
|
||||
struct atomic_dev_params *atomic_params)
|
||||
{
|
||||
/*
|
||||
* NVMe is not supported with given kernel version, so we
|
||||
* have no way to figure out what the current NVMe format
|
||||
* is. In this situation we make a naive assumption that
|
||||
* it's formatted to LBA size 512B, and try to treat it
|
||||
* as regular block device.
|
||||
*/
|
||||
*type = BLOCK_DEVICE_VOLUME;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cas_blk_identify_type_by_bdev(struct block_device *bdev,
|
||||
uint8_t *type, struct atomic_dev_params *atomic_params)
|
||||
{
|
||||
*type = BLOCK_DEVICE_VOLUME;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int cas_blk_open_volume_by_bdev(ocf_volume_t *vol,
|
||||
struct block_device *bdev)
|
||||
{
|
||||
struct atomic_dev_params atomic_params = {0};
|
||||
struct bd_object *bdobj;
|
||||
uint8_t type;
|
||||
int ret;
|
||||
|
||||
ret = cas_blk_identify_type_by_bdev(bdev, &type, &atomic_params);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = ocf_ctx_volume_create(cas_ctx, vol, NULL, type);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
bdobj = bd_object(*vol);
|
||||
|
||||
bdobj->btm_bd = bdev;
|
||||
bdobj->opened_by_bdev = true;
|
||||
|
||||
ocf_volume_open(*vol);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void cas_blk_close_volume(ocf_volume_t vol)
|
||||
{
|
||||
ocf_volume_close(vol);
|
||||
ocf_volume_deinit(vol);
|
||||
env_free(vol);
|
||||
}
|
||||
|
||||
int _cas_blk_identify_type(const char *path, uint8_t *type,
|
||||
struct atomic_dev_params *atomic_params)
|
||||
{
|
||||
struct file *file;
|
||||
int result = 0;
|
||||
|
||||
file = filp_open(path, O_RDONLY, 0);
|
||||
if (IS_ERR(file))
|
||||
return -OCF_ERR_INVAL_VOLUME_TYPE;
|
||||
|
||||
if (S_ISBLK(FILE_INODE(file)->i_mode))
|
||||
*type = BLOCK_DEVICE_VOLUME;
|
||||
else if (S_ISCHR(FILE_INODE(file)->i_mode))
|
||||
*type = NVME_CONTROLLER;
|
||||
else
|
||||
result = -OCF_ERR_INVAL_VOLUME_TYPE;
|
||||
|
||||
filp_close(file, 0);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
if (*type == BLOCK_DEVICE_VOLUME) {
|
||||
result = _cas_detect_blk_type(path, type, atomic_params);
|
||||
if (result < 0)
|
||||
return result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cas_blk_identify_type(const char *path, uint8_t *type)
|
||||
{
|
||||
return _cas_blk_identify_type(path, type, NULL);
|
||||
}
|
||||
|
||||
int cas_blk_identify_type_atomic(const char *path, uint8_t *type,
|
||||
struct atomic_dev_params *atomic_params)
|
||||
{
|
||||
return _cas_blk_identify_type(path, type, atomic_params);
|
||||
}
|
||||
|
148
modules/cas_cache/volume/vol_blk_utils.h
Normal file
148
modules/cas_cache/volume/vol_blk_utils.h
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright(c) 2012-2019 Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
*/
|
||||
|
||||
#ifndef __VOL_BLK_UTILS_H__
|
||||
#define __VOL_BLK_UTILS_H__
|
||||
|
||||
#include "obj_blk.h"
|
||||
#include "context.h"
|
||||
|
||||
static inline bool cas_blk_is_flush_io(unsigned long flags)
|
||||
{
|
||||
if ((flags & OCF_WRITE_FLUSH) == OCF_WRITE_FLUSH)
|
||||
return true;
|
||||
|
||||
if ((flags & OCF_WRITE_FLUSH_FUA) == OCF_WRITE_FLUSH_FUA)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct blkio {
|
||||
int error;
|
||||
atomic_t rq_remaning;
|
||||
atomic_t ref_counter;
|
||||
int32_t dirty;
|
||||
int32_t dir;
|
||||
|
||||
struct blk_data *data; /* IO data buffer */
|
||||
|
||||
/* BIO vector iterator for sending IO */
|
||||
struct bio_vec_iter iter;
|
||||
};
|
||||
|
||||
static inline struct blkio *cas_io_to_blkio(struct ocf_io *io)
|
||||
{
|
||||
return ocf_io_get_priv(io);
|
||||
}
|
||||
|
||||
int cas_blk_io_set_data(struct ocf_io *io, ctx_data_t *data,
|
||||
uint32_t offset);
|
||||
ctx_data_t *cas_blk_io_get_data(struct ocf_io *io);
|
||||
|
||||
int cas_blk_identify_type_by_bdev(struct block_device *bdev,
|
||||
uint8_t *type, struct atomic_dev_params *atomic_params);
|
||||
|
||||
int cas_blk_open_volume_by_bdev(ocf_volume_t *vol,
|
||||
struct block_device *bdev);
|
||||
void cas_blk_close_volume(ocf_volume_t vol);
|
||||
|
||||
int cas_blk_identify_type(const char *path, uint8_t *type);
|
||||
|
||||
int cas_blk_identify_type_atomic(const char *path, uint8_t *type,
|
||||
struct atomic_dev_params *atomic_params);
|
||||
|
||||
static inline void cas_io_iter_init(struct bio_vec_iter *iter,
|
||||
struct bio_vec *vec, uint32_t vec_size)
|
||||
{
|
||||
iter->vec = iter->ivec = vec;
|
||||
iter->vec_size = vec_size;
|
||||
iter->idx = 0;
|
||||
iter->offset = vec->bv_offset;
|
||||
iter->len = vec->bv_len;
|
||||
}
|
||||
|
||||
static inline void cas_io_iter_set(struct bio_vec_iter *iter,
|
||||
struct bio_vec *vec, uint32_t vec_size,
|
||||
uint32_t idx, uint32_t offset, uint32_t len)
|
||||
{
|
||||
iter->vec = vec;
|
||||
iter->vec_size = vec_size;
|
||||
iter->idx = idx;
|
||||
iter->offset = offset;
|
||||
iter->len = len;
|
||||
|
||||
if (iter->idx < vec_size) {
|
||||
iter->ivec = &vec[iter->idx];
|
||||
} else {
|
||||
iter->ivec = NULL;
|
||||
WARN(1, "Setting offset out of BIO vector");
|
||||
}
|
||||
}
|
||||
|
||||
static inline void cas_io_iter_copy_set(struct bio_vec_iter *dst,
|
||||
struct bio_vec_iter *src)
|
||||
{
|
||||
dst->vec = src->vec;
|
||||
dst->vec_size = src->vec_size;
|
||||
dst->idx = src->idx;
|
||||
dst->offset = src->offset;
|
||||
dst->len = src->len;
|
||||
dst->ivec = src->ivec;
|
||||
}
|
||||
|
||||
static inline bool cas_io_iter_is_next(struct bio_vec_iter *iter)
|
||||
{
|
||||
return iter->idx < iter->vec_size ? true : false;
|
||||
/* TODO UNITTEST */
|
||||
}
|
||||
|
||||
static inline uint32_t cas_io_iter_size_done(struct bio_vec_iter *iter)
|
||||
{
|
||||
return iter->idx;
|
||||
/* TODO UNITTEST */
|
||||
}
|
||||
|
||||
static inline uint32_t cas_io_iter_size_left(struct bio_vec_iter *iter)
|
||||
{
|
||||
if (iter->idx < iter->vec_size)
|
||||
return iter->vec_size - iter->idx;
|
||||
return 0;
|
||||
/* TODO UNITTEST */
|
||||
}
|
||||
|
||||
static inline uint32_t cas_io_iter_current_offset(struct bio_vec_iter *iter)
|
||||
{
|
||||
return iter->idx < iter->vec_size ? iter->offset : 0;
|
||||
/* TODO UNITTEST */
|
||||
}
|
||||
|
||||
static inline uint32_t cas_io_iter_current_length(struct bio_vec_iter *iter)
|
||||
{
|
||||
return iter->idx < iter->vec_size ? iter->len : 0;
|
||||
/* TODO UNITTEST */
|
||||
}
|
||||
|
||||
static inline struct page *cas_io_iter_current_page(struct bio_vec_iter *iter)
|
||||
{
|
||||
return iter->idx < iter->vec_size ? iter->ivec->bv_page : NULL;
|
||||
/* TODO UNITTEST */
|
||||
}
|
||||
|
||||
uint32_t cas_io_iter_cpy(struct bio_vec_iter *dst, struct bio_vec_iter *src,
|
||||
uint32_t bytes);
|
||||
|
||||
uint32_t cas_io_iter_cpy_from_data(struct bio_vec_iter *dst,
|
||||
const void *src, uint32_t bytes);
|
||||
|
||||
uint32_t cas_io_iter_cpy_to_data(void *dst, struct bio_vec_iter *src,
|
||||
uint32_t bytes);
|
||||
|
||||
uint32_t cas_io_iter_move(struct bio_vec_iter *iter,
|
||||
uint32_t bytes);
|
||||
|
||||
uint32_t cas_io_iter_zero(struct bio_vec_iter *iter, uint32_t bytes);
|
||||
|
||||
#endif /* __VOL_BLK_UTILS_H__ */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user