mirror of
https://github.com/SCST-project/scst.git
synced 2026-05-14 09:11:27 +00:00
401 lines
9.1 KiB
C
401 lines
9.1 KiB
C
/*
|
|
* Copyright (C) 2005 FUJITA Tomonori <tomof@acm.org>
|
|
* Copyright (C) 2007 - 2018 Vladislav Bolkhovitin
|
|
* Copyright (C) 2007 - 2018 Western Digital Corporation
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
|
|
#include "iscsid.h"
|
|
|
|
/* Taken from Linux kernel sources */
|
|
size_t strlcpy(char *dest, const char *src, size_t size)
|
|
{
|
|
size_t ret = strlen(src);
|
|
|
|
if (size) {
|
|
size_t len = (ret >= size) ? size - 1 : ret;
|
|
|
|
memcpy(dest, src, len);
|
|
dest[len] = '\0';
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int params_index_by_name(const char *name, const struct iscsi_key *keys)
|
|
{
|
|
int i, err = -ENOENT;
|
|
|
|
for (i = 0; keys[i].name; i++) {
|
|
if (!strcasecmp(keys[i].name, name)) {
|
|
err = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int params_index_by_name_numwild(const char *name, const struct iscsi_key *keys)
|
|
{
|
|
int i, err = -ENOENT;
|
|
|
|
for (i = 0; keys[i].name; i++) {
|
|
if (!strncasecmp(keys[i].name, name, strlen(keys[i].name))) {
|
|
int j;
|
|
|
|
if (strlen(keys[i].name) > strlen(name))
|
|
continue;
|
|
for (j = strlen(keys[i].name); j < strlen(name); j++) {
|
|
if (!isdigit(name[j]))
|
|
goto next;
|
|
}
|
|
err = i;
|
|
break;
|
|
next:
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
void params_set_defaults(unsigned int *params, const struct iscsi_key *keys)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; keys[i].name; i++)
|
|
params[i] = keys[i].local_def;
|
|
|
|
return;
|
|
}
|
|
|
|
static int range_val_to_str(unsigned int val, char *str, int len)
|
|
{
|
|
snprintf(str, len, "%u", val);
|
|
return 0;
|
|
}
|
|
|
|
static int range_str_to_val(const char *str, unsigned int *val)
|
|
{
|
|
*val = strtol(str, NULL, 0);
|
|
return 0;
|
|
}
|
|
|
|
static int bool_val_to_str(unsigned int val, char *str, int len)
|
|
{
|
|
int err = 0;
|
|
|
|
switch (val) {
|
|
case 0:
|
|
strlcpy(str, "No", len);
|
|
break;
|
|
case 1:
|
|
strlcpy(str, "Yes", len);
|
|
break;
|
|
default:
|
|
err = -EINVAL;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int bool_str_to_val(const char *str, unsigned int *val)
|
|
{
|
|
int err = 0;
|
|
|
|
if (!strcmp(str, "Yes"))
|
|
*val = 1;
|
|
else if (!strcmp(str, "No"))
|
|
*val = 0;
|
|
else
|
|
err = -EINVAL;
|
|
|
|
return err;
|
|
}
|
|
|
|
static int or_set_val(struct iscsi_param *param, int idx, unsigned int *val)
|
|
{
|
|
*val |= param[idx].val;
|
|
param[idx].val = *val;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int and_set_val(struct iscsi_param *param, int idx, unsigned int *val)
|
|
{
|
|
*val &= param[idx].val;
|
|
param[idx].val = *val;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int num_check_val(const struct iscsi_key *key, unsigned int *val)
|
|
{
|
|
int err = 0;
|
|
|
|
if (*val < key->min) {
|
|
*val = key->min;
|
|
err = -EINVAL;
|
|
} else if (*val > key->max) {
|
|
*val = key->max;
|
|
err = -EINVAL;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int minimum_set_val(struct iscsi_param *param, int idx, unsigned int *val)
|
|
{
|
|
if (*val > param[idx].val)
|
|
*val = param[idx].val;
|
|
param[idx].val = *val;
|
|
return 0;
|
|
}
|
|
|
|
static int maximum_set_val(struct iscsi_param *param, int idx, unsigned int *val)
|
|
{
|
|
if (param[idx].val > *val)
|
|
*val = param[idx].val;
|
|
param[idx].val = *val;
|
|
return 0;
|
|
}
|
|
|
|
static int digest_val_to_str(unsigned int val, char *str, int len)
|
|
{
|
|
int pos = 0;
|
|
|
|
if (val & DIGEST_NONE) {
|
|
len -= pos;
|
|
pos = snprintf(&str[pos], len, "None");
|
|
}
|
|
|
|
if (pos >= len)
|
|
goto out;
|
|
|
|
if (val & DIGEST_CRC32C) {
|
|
len -= pos;
|
|
pos = snprintf(&str[pos], len, "%s%s",
|
|
(pos != 0) ? "," : "", "CRC32C");
|
|
}
|
|
|
|
if (pos >= len)
|
|
goto out;
|
|
|
|
if (pos == 0)
|
|
pos = snprintf(&str[0], len, "Unknown");
|
|
|
|
out:
|
|
return 0;
|
|
}
|
|
|
|
static int digest_str_to_val(const char *str, unsigned int *val)
|
|
{
|
|
int err = 0;
|
|
const char *p, *q;
|
|
|
|
p = str;
|
|
*val = 0;
|
|
do {
|
|
while ((*p != '\0') && isspace(*p))
|
|
p++;
|
|
if (!strncasecmp(p, "None", strlen("None")))
|
|
*val |= DIGEST_NONE;
|
|
else if (!strncasecmp(p, "CRC32C", strlen("CRC32C")))
|
|
*val |= DIGEST_CRC32C;
|
|
else {
|
|
err = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
if ((q = strchr(p, ',')))
|
|
p = q + 1;
|
|
} while (q);
|
|
|
|
if (*val == 0)
|
|
*val = DIGEST_NONE;
|
|
|
|
return err;
|
|
}
|
|
|
|
static int digest_set_val(struct iscsi_param *param, int idx, unsigned int *val)
|
|
{
|
|
if (*val & DIGEST_CRC32C && param[idx].val & DIGEST_CRC32C)
|
|
*val = DIGEST_CRC32C;
|
|
else
|
|
*val = DIGEST_NONE;
|
|
|
|
param[idx].val = *val;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int marker_val_to_str(unsigned int val, char *str, int len)
|
|
{
|
|
if (val == 0)
|
|
strlcpy(str, "Irrelevant", len);
|
|
else
|
|
strlcpy(str, "Reject", len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int marker_set_val(struct iscsi_param *params, int idx, unsigned int *val)
|
|
{
|
|
if ((idx == key_ofmarkint && params[key_ofmarker].key_state == KEY_STATE_DONE) ||
|
|
(idx == key_ifmarkint && params[key_ifmarker].key_state == KEY_STATE_DONE))
|
|
*val = 0;
|
|
else
|
|
*val = 1;
|
|
|
|
params[idx].val = *val;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int params_val_to_str(const struct iscsi_key *keys, int idx, unsigned int val,
|
|
char *str, int len)
|
|
{
|
|
if (keys[idx].ops->val_to_str)
|
|
return keys[idx].ops->val_to_str(val, str, len);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int params_str_to_val(const struct iscsi_key *keys, int idx, const char *str,
|
|
unsigned int *val)
|
|
{
|
|
if (!strcmp(":default:", str)) {
|
|
*val = keys[idx].local_def;
|
|
return 0;
|
|
} else if (keys[idx].ops->str_to_val)
|
|
return keys[idx].ops->str_to_val(str, val);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int params_check_val(const struct iscsi_key *keys, int idx, unsigned int *val)
|
|
{
|
|
if (keys[idx].ops->check_val)
|
|
return keys[idx].ops->check_val(&keys[idx], val);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int params_set_val(struct iscsi_key *keys, struct iscsi_param *param,
|
|
int idx, unsigned int *val)
|
|
{
|
|
if (keys[idx].ops->set_val)
|
|
return keys[idx].ops->set_val(param, idx, val);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static struct iscsi_key_ops minimum_ops = {
|
|
.val_to_str = range_val_to_str,
|
|
.str_to_val = range_str_to_val,
|
|
.check_val = num_check_val,
|
|
.set_val = minimum_set_val,
|
|
};
|
|
|
|
static struct iscsi_key_ops maximum_ops = {
|
|
.val_to_str = range_val_to_str,
|
|
.str_to_val = range_str_to_val,
|
|
.check_val = num_check_val,
|
|
.set_val = maximum_set_val,
|
|
};
|
|
|
|
static struct iscsi_key_ops or_ops = {
|
|
.val_to_str = bool_val_to_str,
|
|
.str_to_val = bool_str_to_val,
|
|
.set_val = or_set_val,
|
|
};
|
|
|
|
static struct iscsi_key_ops and_ops = {
|
|
.val_to_str = bool_val_to_str,
|
|
.str_to_val = bool_str_to_val,
|
|
.set_val = and_set_val,
|
|
};
|
|
|
|
static struct iscsi_key_ops digest_ops = {
|
|
.val_to_str = digest_val_to_str,
|
|
.str_to_val = digest_str_to_val,
|
|
.set_val = digest_set_val,
|
|
};
|
|
|
|
static struct iscsi_key_ops marker_ops = {
|
|
.val_to_str = marker_val_to_str,
|
|
.set_val = marker_set_val,
|
|
};
|
|
|
|
#define SET_KEY_VALUES(x) DEFAULT_##x, DEFAULT_##x, MIN_##x, MAX_##x
|
|
|
|
/*
|
|
* List of local target keys with initial values.
|
|
* Must match corresponding key_* enum in iscsi_scst.h!!
|
|
*
|
|
* Updating this array don't forget to update tgt_params_check() in
|
|
* the kernel as well!
|
|
*/
|
|
struct iscsi_key target_keys[] = {
|
|
/* name, rfc_def, local_def, min, max, show_in_sysfs, ops */
|
|
{"QueuedCommands", SET_KEY_VALUES(NR_QUEUED_CMNDS), 1, &minimum_ops},
|
|
{"RspTimeout", SET_KEY_VALUES(RSP_TIMEOUT), 1, &minimum_ops},
|
|
{"NopInInterval", SET_KEY_VALUES(NOP_IN_INTERVAL), 1, &minimum_ops},
|
|
{"NopInTimeout", SET_KEY_VALUES(NOP_IN_TIMEOUT), 1, &minimum_ops},
|
|
{"MaxSessions", 0, 0, 0, 65535, 1, &minimum_ops},
|
|
{NULL,},
|
|
};
|
|
|
|
/*
|
|
* List of iSCSI RFC specified session keys with initial values.
|
|
* Must match corresponding key_* enum in iscsi_scst.h!!
|
|
*
|
|
* Updating this array don't forget to update sess_params_check() in
|
|
* the kernel as well!
|
|
*/
|
|
struct iscsi_key session_keys[] = {
|
|
/* name, rfc_def, local_def, min, max, show_in_sysfs, ops */
|
|
{"InitialR2T", 1, 0, 0, 1, 1, &or_ops},
|
|
{"ImmediateData", 1, 1, 0, 1, 1, &and_ops},
|
|
{"MaxConnections", 1, 1, 1, 1, 0, &minimum_ops},
|
|
{"MaxRecvDataSegmentLength", 8192, -1, 512, -1, 1, &minimum_ops},
|
|
{"MaxXmitDataSegmentLength", 8192, -1, 512, -1, 1, &minimum_ops},
|
|
{"MaxBurstLength", 262144, -1, 512, -1, 1, &minimum_ops},
|
|
{"FirstBurstLength", 65536, 65536, 512, -1, 1, &minimum_ops},
|
|
{"DefaultTime2Wait", 2, 0, 0, 0, 0, &maximum_ops},
|
|
{"DefaultTime2Retain", 20, 0, 0, 0, 0, &minimum_ops},
|
|
{"MaxOutstandingR2T", 1, 32, 1, 65535, 1, &minimum_ops},
|
|
{"DataPDUInOrder", 1, 0, 0, 1, 0, &or_ops},
|
|
{"DataSequenceInOrder", 1, 0, 0, 1, 0, &or_ops},
|
|
{"ErrorRecoveryLevel", 0, 0, 0, 0, 0, &minimum_ops},
|
|
{"HeaderDigest", DIGEST_NONE, DIGEST_NONE, DIGEST_NONE, DIGEST_ALL, 1, &digest_ops},
|
|
{"DataDigest", DIGEST_NONE, DIGEST_NONE, DIGEST_NONE, DIGEST_ALL, 1, &digest_ops},
|
|
{"OFMarker", 0, 0, 0, 0, 0, &and_ops},
|
|
{"IFMarker", 0, 0, 0, 0, 0, &and_ops},
|
|
{"OFMarkInt", 2048, 2048, 1, 65535, 0, &marker_ops},
|
|
{"IFMarkInt", 2048, 2048, 1, 65535, 0, &marker_ops},
|
|
{"RDMAExtensions", 0, 0, 0, 0, 1, &and_ops},
|
|
{"TargetRecvDataSegmentLength", 8192, 512, 512, -1, 0, &minimum_ops},
|
|
{"InitiatorRecvDataSegmentLength", 8192, -1, 512, -1, 0, &minimum_ops},
|
|
{"MaxAHSLength", 256, 0, 0, -1, 0, &minimum_ops},
|
|
{"TaggedBufferForSolicitedDataOnly", 0, 0, 0, 0, 0, &and_ops},
|
|
{"iSERHelloRequired", 0, 0, 0, 0, 0, &and_ops},
|
|
{"MaxOutstandingUnexpectedPDUs", 0, 0, 0, -1, 0, &minimum_ops},
|
|
{NULL,},
|
|
};
|