blob: 77b1a82fb49d300aef97b7b566f4bb9acfe63295 [file] [log] [blame]
/*
* 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 "iscsi_trace_flag.h"
#include "iscsi.h"
#include "digest.h"
#define CHECK_PARAM(info, iparams, word, min, max) \
do { \
if (!(info)->partial || ((info)->partial & 1 << key_##word)) { \
TRACE_DBG("%s: %u", #word, (iparams)[key_##word]); \
if ((iparams)[key_##word] < (min) || \
(iparams)[key_##word] > (max)) { \
if ((iparams)[key_##word] < (min)) { \
(iparams)[key_##word] = (min); \
PRINT_WARNING("%s: %u is too small, resetting " \
"it to allowed min %u", \
#word, (iparams)[key_##word], (min)); \
} else { \
PRINT_WARNING("%s: %u is too big, resetting " \
"it to allowed max %u", \
#word, (iparams)[key_##word], (max)); \
(iparams)[key_##word] = (max); \
} \
} \
} \
} while (0)
#define SET_PARAM(params, info, iparams, word) \
({ \
int changed = 0; \
if (!(info)->partial || ((info)->partial & 1 << key_##word)) { \
if ((params)->word != (iparams)[key_##word]) \
changed = 1; \
(params)->word = (iparams)[key_##word]; \
TRACE_DBG("%s set to %u", #word, (params)->word); \
} \
changed; \
})
#define GET_PARAM(params, info, iparams, word) \
((iparams)[key_##word] = (params)->word)
const char *iscsi_get_bool_value(int val)
{
if (val)
return "Yes";
else
return "No";
}
const char *iscsi_get_digest_name(int val, char *res)
{
int pos = 0;
if (val & DIGEST_NONE)
pos = sprintf(&res[pos], "None");
if (val & DIGEST_CRC32C)
pos += sprintf(&res[pos], "%s%s", (pos != 0) ? ", " : "",
"CRC32C");
if (pos == 0)
sprintf(&res[pos], "Unknown");
return res;
}
static void log_params(struct iscsi_sess_params *params)
{
char hdigest_name[64], ddigest_name[64];
PRINT_INFO("Negotiated parameters: InitialR2T %s, ImmediateData %s, MaxConnections %d, MaxRecvDataSegmentLength %d, MaxXmitDataSegmentLength %d, ",
iscsi_get_bool_value(params->initial_r2t),
iscsi_get_bool_value(params->immediate_data),
params->max_connections,
params->max_recv_data_length, params->max_xmit_data_length);
PRINT_INFO(" MaxBurstLength %d, FirstBurstLength %d, DefaultTime2Wait %d, DefaultTime2Retain %d, ",
params->max_burst_length, params->first_burst_length,
params->default_wait_time, params->default_retain_time);
PRINT_INFO(" MaxOutstandingR2T %d, DataPDUInOrder %s, DataSequenceInOrder %s, ErrorRecoveryLevel %d, ",
params->max_outstanding_r2t,
iscsi_get_bool_value(params->data_pdu_inorder),
iscsi_get_bool_value(params->data_sequence_inorder),
params->error_recovery_level);
PRINT_INFO(" HeaderDigest %s, DataDigest %s, OFMarker %s, IFMarker %s, OFMarkInt %d, IFMarkInt %d, RDMAExtensions %s",
iscsi_get_digest_name(params->header_digest, hdigest_name),
iscsi_get_digest_name(params->data_digest, ddigest_name),
iscsi_get_bool_value(params->ofmarker),
iscsi_get_bool_value(params->ifmarker),
params->ofmarkint, params->ifmarkint,
iscsi_get_bool_value(params->rdma_extensions));
}
/* target_mutex supposed to be locked */
static void sess_params_check(struct iscsi_kern_params_info *info)
{
int32_t *iparams = info->session_params;
const int max_len = ISCSI_CONN_IOV_MAX * PAGE_SIZE;
/*
* This is only kernel sanity check. Actual data validity checks
* performed in the user space.
*/
CHECK_PARAM(info, iparams, initial_r2t, 0, 1);
CHECK_PARAM(info, iparams, immediate_data, 0, 1);
CHECK_PARAM(info, iparams, max_connections, 1, 1);
CHECK_PARAM(info, iparams, max_recv_data_length, 512, max_len);
CHECK_PARAM(info, iparams, max_xmit_data_length, 512, max_len);
CHECK_PARAM(info, iparams, max_burst_length, 512, max_len);
CHECK_PARAM(info, iparams, first_burst_length, 512, max_len);
CHECK_PARAM(info, iparams, max_outstanding_r2t, 1, 65535);
CHECK_PARAM(info, iparams, error_recovery_level, 0, 0);
CHECK_PARAM(info, iparams, data_pdu_inorder, 0, 1);
CHECK_PARAM(info, iparams, data_sequence_inorder, 0, 1);
digest_alg_available(&iparams[key_header_digest]);
digest_alg_available(&iparams[key_data_digest]);
CHECK_PARAM(info, iparams, ofmarker, 0, 0);
CHECK_PARAM(info, iparams, ifmarker, 0, 0);
/* iSER related parameters */
CHECK_PARAM(info, iparams, rdma_extensions, 0, 1);
CHECK_PARAM(info, iparams, target_recv_data_length, 512, max_len);
CHECK_PARAM(info, iparams, initiator_recv_data_length, 512, max_len);
return;
}
/* target_mutex supposed to be locked */
static void sess_params_set(struct iscsi_sess_params *params,
struct iscsi_kern_params_info *info)
{
int32_t *iparams = info->session_params;
SET_PARAM(params, info, iparams, initial_r2t);
SET_PARAM(params, info, iparams, immediate_data);
SET_PARAM(params, info, iparams, max_connections);
SET_PARAM(params, info, iparams, max_recv_data_length);
SET_PARAM(params, info, iparams, max_xmit_data_length);
SET_PARAM(params, info, iparams, max_burst_length);
SET_PARAM(params, info, iparams, first_burst_length);
SET_PARAM(params, info, iparams, default_wait_time);
SET_PARAM(params, info, iparams, default_retain_time);
SET_PARAM(params, info, iparams, max_outstanding_r2t);
SET_PARAM(params, info, iparams, data_pdu_inorder);
SET_PARAM(params, info, iparams, data_sequence_inorder);
SET_PARAM(params, info, iparams, error_recovery_level);
SET_PARAM(params, info, iparams, header_digest);
SET_PARAM(params, info, iparams, data_digest);
SET_PARAM(params, info, iparams, ofmarker);
SET_PARAM(params, info, iparams, ifmarker);
SET_PARAM(params, info, iparams, ofmarkint);
SET_PARAM(params, info, iparams, ifmarkint);
/* iSER related parameters */
SET_PARAM(params, info, iparams, rdma_extensions);
SET_PARAM(params, info, iparams, target_recv_data_length);
SET_PARAM(params, info, iparams, initiator_recv_data_length);
return;
}
static void sess_params_get(struct iscsi_sess_params *params,
struct iscsi_kern_params_info *info)
{
int32_t *iparams = info->session_params;
GET_PARAM(params, info, iparams, initial_r2t);
GET_PARAM(params, info, iparams, immediate_data);
GET_PARAM(params, info, iparams, max_connections);
GET_PARAM(params, info, iparams, max_recv_data_length);
GET_PARAM(params, info, iparams, max_xmit_data_length);
GET_PARAM(params, info, iparams, max_burst_length);
GET_PARAM(params, info, iparams, first_burst_length);
GET_PARAM(params, info, iparams, default_wait_time);
GET_PARAM(params, info, iparams, default_retain_time);
GET_PARAM(params, info, iparams, max_outstanding_r2t);
GET_PARAM(params, info, iparams, data_pdu_inorder);
GET_PARAM(params, info, iparams, data_sequence_inorder);
GET_PARAM(params, info, iparams, error_recovery_level);
GET_PARAM(params, info, iparams, header_digest);
GET_PARAM(params, info, iparams, data_digest);
GET_PARAM(params, info, iparams, ofmarker);
GET_PARAM(params, info, iparams, ifmarker);
GET_PARAM(params, info, iparams, ofmarkint);
GET_PARAM(params, info, iparams, ifmarkint);
/* iSER related parameters */
GET_PARAM(params, info, iparams, rdma_extensions);
GET_PARAM(params, info, iparams, target_recv_data_length);
GET_PARAM(params, info, iparams, initiator_recv_data_length);
return;
}
/* target_mutex supposed to be locked */
static void tgt_params_check(struct iscsi_session *session,
struct iscsi_kern_params_info *info)
{
int32_t *iparams = info->target_params;
unsigned int rsp_timeout, nop_in_timeout;
/*
* This is only kernel sanity check. Actual data validity checks
* performed in the user space.
*/
CHECK_PARAM(info, iparams, queued_cmnds, MIN_NR_QUEUED_CMNDS,
MAX_NR_QUEUED_CMNDS);
CHECK_PARAM(info, iparams, rsp_timeout, MIN_RSP_TIMEOUT,
MAX_RSP_TIMEOUT);
CHECK_PARAM(info, iparams, nop_in_interval, MIN_NOP_IN_INTERVAL,
MAX_NOP_IN_INTERVAL);
CHECK_PARAM(info, iparams, nop_in_timeout, MIN_NOP_IN_TIMEOUT,
MAX_NOP_IN_TIMEOUT);
/*
* We adjust too long timeout in req_add_to_write_timeout_list()
* only for NOPs, so check and warn if this assumption isn't honored.
*/
if (!info->partial || (info->partial & 1 << key_rsp_timeout))
rsp_timeout = iparams[key_rsp_timeout];
else
rsp_timeout = session->tgt_params.rsp_timeout;
if (!info->partial || (info->partial & 1 << key_nop_in_timeout))
nop_in_timeout = iparams[key_nop_in_timeout];
else
nop_in_timeout = session->tgt_params.nop_in_timeout;
if (nop_in_timeout > rsp_timeout)
PRINT_WARNING("%s", "RspTimeout should be >= NopInTimeout, otherwise data transfer failure could take up to NopInTimeout long to detect");
return;
}
/* target_mutex supposed to be locked */
static int iscsi_tgt_params_set(struct iscsi_session *session,
struct iscsi_kern_params_info *info, int set)
{
struct iscsi_tgt_params *params = &session->tgt_params;
int32_t *iparams = info->target_params;
lockdep_assert_held(&session->target->target_mutex);
if (set) {
struct iscsi_conn *conn;
tgt_params_check(session, info);
SET_PARAM(params, info, iparams, queued_cmnds);
SET_PARAM(params, info, iparams, rsp_timeout);
SET_PARAM(params, info, iparams, nop_in_interval);
SET_PARAM(params, info, iparams, nop_in_timeout);
PRINT_INFO("Target parameters set for session %llx: QueuedCommands %d, Response timeout %d, Nop-In interval %d, Nop-In timeout %d", session->sid,
params->queued_cmnds, params->rsp_timeout,
params->nop_in_interval, params->nop_in_timeout);
list_for_each_entry(conn, &session->conn_list,
conn_list_entry) {
conn->data_rsp_timeout =
session->tgt_params.rsp_timeout * HZ;
conn->nop_in_interval =
session->tgt_params.nop_in_interval * HZ;
conn->nop_in_timeout =
session->tgt_params.nop_in_timeout * HZ;
spin_lock_bh(&conn->conn_thr_pool->rd_lock);
if (!conn->closing && (conn->nop_in_interval > 0)) {
TRACE_DBG("Schedule Nop-In work for conn %p",
conn);
schedule_delayed_work(&conn->nop_in_delayed_work,
conn->nop_in_interval + ISCSI_ADD_SCHED_TIME);
}
spin_unlock_bh(&conn->conn_thr_pool->rd_lock);
}
} else {
GET_PARAM(params, info, iparams, queued_cmnds);
GET_PARAM(params, info, iparams, rsp_timeout);
GET_PARAM(params, info, iparams, nop_in_interval);
GET_PARAM(params, info, iparams, nop_in_timeout);
}
return 0;
}
/* target_mutex supposed to be locked */
static int iscsi_sess_params_set(struct iscsi_session *session,
struct iscsi_kern_params_info *info, int set)
{
struct iscsi_sess_params *params;
if (set)
sess_params_check(info);
params = &session->sess_params;
if (set) {
sess_params_set(params, info);
log_params(params);
} else
sess_params_get(params, info);
return 0;
}
/* target_mutex supposed to be locked */
int iscsi_params_set(struct iscsi_target *target,
struct iscsi_kern_params_info *info, int set)
{
int err;
struct iscsi_session *session;
lockdep_assert_held(&target->target_mutex);
if (info->sid == 0) {
PRINT_ERROR("sid must not be %d", 0);
err = -EINVAL;
goto out;
}
session = session_lookup(target, info->sid);
if (session == NULL) {
PRINT_ERROR("Session for sid %llx not found", info->sid);
err = -ENOENT;
goto out;
}
if (set && !list_empty(&session->conn_list) &&
(info->params_type != key_target)) {
err = -EBUSY;
goto out;
}
if (info->params_type == key_session)
err = iscsi_sess_params_set(session, info, set);
else if (info->params_type == key_target)
err = iscsi_tgt_params_set(session, info, set);
else
err = -EINVAL;
out:
return err;
}