Initial commit

Signed-off-by: Robert Baldyga <robert.baldyga@intel.com>
This commit is contained in:
Robert Baldyga 2019-03-29 08:39:34 +01:00
commit 94e8ca09e0
140 changed files with 37144 additions and 0 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "ocf"]
path = ocf
url = git@github.com:Open-CAS/ocf.git

15
Makefile Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

297
casadm/cas_lib.h Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

483
casadm/csvparse.c Normal file
View 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
View 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
View 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);
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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);

View 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;
}

View 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
View 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 rst 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
View 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 rst 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 rst 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
View 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);

View 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__ */

View 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__ */

View 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);
}
}

View 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__ */

View 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__ */

View 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);
}
}

View 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__ */

View 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__ */

View 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__ */

View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

117
casadm/statistics_view.c Normal file
View 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
View 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 */

View 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;
}

View 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

View 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;
}

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,3 @@
include/
src/

View 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))

View 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

View 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;
}

View 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

View 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
View 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);
}

View 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__ */

View 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);
}

View 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

View 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 */

View 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
View 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
View 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
View 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__ */

View 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__ */

View 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;
}
}

View 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
View 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);
}

View 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__ */

View 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__ */

View 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;
}

View 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_ */

View 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;
}

View 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_ */

View 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

View 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_ */

View 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

View 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_ */

View 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;
}

View 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_ */

View 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, &current_rpool->list);
atomic_inc(&current_rpool->count);
}
CAS_DEBUG_PARAM("Added [%d] pre allocated items to reserve poll [%s]"
" for cpu %d", atomic_read(&current_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(&current_rpool->count))
continue;
list_for_each_safe(item, next, &current_rpool->list) {
entry = RPOOL_ITEM_TO_ENTRY(rpool_master, item);
list_del(item);
rpool_del(allocator_ctx, entry);
atomic_dec(&current_rpool->count);
}
if (atomic_read(&current_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(&current_rpool->lock);
INIT_LIST_HEAD(&current_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(&current_rpool->lock, flags);
if (!list_empty(&current_rpool->list)) {
item = LIST_FIRST_ITEM(current_rpool->list);
entry = RPOOL_ITEM_TO_ENTRY(rpool_master, item);
list_del(item);
atomic_dec(&current_rpool->count);
}
spin_unlock_irqrestore(&current_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(&current_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(&current_rpool->lock, flags);
if (atomic_read(&current_rpool->count) >= rpool_master->limit) {
ret = 1;
goto error;
}
item = RPOOL_ENTRY_TO_ITEM(rpool_master, entry);
list_add_tail(item, &current_rpool->list);
atomic_inc(&current_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(&current_rpool->count));
spin_unlock_irqrestore(&current_rpool->lock, flags);
return ret;
}

View 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__ */

View 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__ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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__ */

View 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);
}

View 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