mirror of
https://github.com/SCST-project/scst.git
synced 2026-05-22 05:01:27 +00:00
The SCST has dropped support for kernels older than 3.10.0 (RHEL 7 / Centos 7) since SCST v3.6.
512 lines
12 KiB
C
512 lines
12 KiB
C
/*
|
|
* This file is part of iser target kernel module.
|
|
*
|
|
* Copyright (c) 2013 - 2014 Mellanox Technologies. All rights reserved.
|
|
* Copyright (c) 2013 - 2014 Yan Burman (yanb@mellanox.com)
|
|
*
|
|
* This software is available to you under a choice of one of two
|
|
* licenses. You may choose to be licensed under the terms of the GNU
|
|
* General Public License (GPL) Version 2, available from the file
|
|
* COPYING in the main directory of this source tree, or the
|
|
* OpenIB.org BSD license below:
|
|
*
|
|
* Redistribution and use in source and binary forms, with or
|
|
* without modification, are permitted provided that the following
|
|
* conditions are met:
|
|
*
|
|
* - Redistributions of source code must retain the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer.
|
|
*
|
|
* - Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer in the documentation and/or other materials
|
|
* provided with the distribution.
|
|
*
|
|
* 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 <linux/module.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/init.h>
|
|
#include <linux/in.h>
|
|
#include <linux/in6.h>
|
|
|
|
#ifdef INSIDE_KERNEL_TREE
|
|
#include <scst/iscsit_transport.h>
|
|
#else
|
|
#include "iscsit_transport.h"
|
|
#endif
|
|
#include "isert_dbg.h"
|
|
#include "isert.h"
|
|
#include "iser.h"
|
|
#include "iser_datamover.h"
|
|
|
|
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
|
|
unsigned long isert_trace_flag = ISERT_DEFAULT_LOG_FLAGS;
|
|
#endif
|
|
|
|
static unsigned int isert_nr_devs = ISERT_NR_DEVS;
|
|
module_param(isert_nr_devs, uint, S_IRUGO);
|
|
MODULE_PARM_DESC(isert_nr_devs,
|
|
"Maximum concurrent number of connection requests to handle (up to 999).");
|
|
|
|
static void isert_mark_conn_closed(struct iscsi_conn *conn, int flags)
|
|
{
|
|
TRACE_ENTRY();
|
|
if (flags & ISCSI_CONN_ACTIVE_CLOSE)
|
|
conn->active_close = 1;
|
|
if (flags & ISCSI_CONN_DELETING)
|
|
conn->deleting = 1;
|
|
|
|
conn->read_state = 0;
|
|
|
|
if (!conn->closing) {
|
|
conn->closing = 1;
|
|
schedule_work(&conn->close_work);
|
|
}
|
|
|
|
TRACE_EXIT();
|
|
}
|
|
|
|
static void isert_close_conn(struct iscsi_conn *conn, int flags)
|
|
{
|
|
struct isert_conn_dev *dev;
|
|
|
|
dev = isert_get_priv(conn);
|
|
if (dev)
|
|
dev->state = CS_DISCONNECTED;
|
|
}
|
|
|
|
static int isert_receive_cmnd_data(struct iscsi_cmnd *cmnd)
|
|
{
|
|
#ifdef CONFIG_SCST_EXTRACHECKS
|
|
if (cmnd->scst_state == ISCSI_CMD_STATE_RX_CMD)
|
|
TRACE_DBG("cmnd %p is still in RX_CMD state",
|
|
cmnd);
|
|
#endif
|
|
EXTRACHECKS_BUG_ON(cmnd->scst_state != ISCSI_CMD_STATE_AFTER_PREPROC);
|
|
return 0;
|
|
}
|
|
|
|
static void isert_update_len_sn(struct iscsi_cmnd *cmnd)
|
|
{
|
|
TRACE_ENTRY();
|
|
|
|
iscsi_cmnd_set_length(&cmnd->pdu);
|
|
switch (cmnd_opcode(cmnd)) {
|
|
case ISCSI_OP_NOP_IN:
|
|
if (cmnd->pdu.bhs.itt == ISCSI_RESERVED_TAG)
|
|
cmnd->pdu.bhs.sn = (__force u32)cmnd_set_sn(cmnd, 0);
|
|
else
|
|
cmnd_set_sn(cmnd, 1);
|
|
break;
|
|
case ISCSI_OP_SCSI_RSP:
|
|
cmnd_set_sn(cmnd, 1);
|
|
break;
|
|
case ISCSI_OP_SCSI_TASK_MGT_RSP:
|
|
cmnd_set_sn(cmnd, 1);
|
|
break;
|
|
case ISCSI_OP_TEXT_RSP:
|
|
cmnd_set_sn(cmnd, 1);
|
|
break;
|
|
case ISCSI_OP_SCSI_DATA_IN:
|
|
{
|
|
struct iscsi_data_in_hdr *rsp =
|
|
(struct iscsi_data_in_hdr *)&cmnd->pdu.bhs;
|
|
|
|
cmnd_set_sn(cmnd, (rsp->flags & ISCSI_FLG_FINAL) ? 1 : 0);
|
|
break;
|
|
}
|
|
case ISCSI_OP_LOGOUT_RSP:
|
|
cmnd_set_sn(cmnd, 1);
|
|
break;
|
|
case ISCSI_OP_R2T:
|
|
cmnd->pdu.bhs.sn = (__force u32)cmnd_set_sn(cmnd, 0);
|
|
break;
|
|
case ISCSI_OP_ASYNC_MSG:
|
|
cmnd_set_sn(cmnd, 1);
|
|
break;
|
|
case ISCSI_OP_REJECT:
|
|
cmnd_set_sn(cmnd, 1);
|
|
break;
|
|
default:
|
|
PRINT_ERROR("Unexpected cmnd op %x", cmnd_opcode(cmnd));
|
|
break;
|
|
}
|
|
|
|
TRACE_EXIT();
|
|
}
|
|
|
|
static int isert_process_all_writes(struct iscsi_conn *conn)
|
|
{
|
|
struct iscsi_cmnd *cmnd;
|
|
int res = 0;
|
|
|
|
TRACE_ENTRY();
|
|
|
|
while ((cmnd = iscsi_get_send_cmnd(conn)) != NULL) {
|
|
isert_update_len_sn(cmnd);
|
|
conn_get(conn);
|
|
isert_pdu_tx(cmnd);
|
|
}
|
|
|
|
TRACE_EXIT_RES(res);
|
|
return res;
|
|
}
|
|
|
|
static int isert_send_locally(struct iscsi_cmnd *req, unsigned int cmd_count)
|
|
{
|
|
int res = 0;
|
|
|
|
TRACE_ENTRY();
|
|
|
|
req_cmnd_pre_release(req);
|
|
res = isert_process_all_writes(req->conn);
|
|
cmnd_put(req);
|
|
|
|
TRACE_EXIT_RES(res);
|
|
return res;
|
|
}
|
|
|
|
static struct iscsi_cmnd *isert_cmnd_alloc(struct iscsi_conn *conn,
|
|
struct iscsi_cmnd *parent)
|
|
{
|
|
struct iscsi_cmnd *cmnd;
|
|
|
|
TRACE_ENTRY();
|
|
|
|
if (likely(parent))
|
|
cmnd = isert_alloc_scsi_rsp_pdu(conn);
|
|
else
|
|
cmnd = isert_alloc_scsi_fake_pdu(conn);
|
|
|
|
iscsi_cmnd_init(conn, cmnd, parent);
|
|
|
|
TRACE_EXIT();
|
|
return cmnd;
|
|
}
|
|
|
|
static void isert_cmnd_free(struct iscsi_cmnd *cmnd)
|
|
{
|
|
struct isert_cmnd *isert_cmnd = container_of(cmnd, struct isert_cmnd,
|
|
iscsi);
|
|
|
|
TRACE_ENTRY();
|
|
|
|
#ifdef CONFIG_SCST_EXTRACHECKS
|
|
if (unlikely(cmnd->on_write_list || cmnd->on_write_timeout_list)) {
|
|
struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd);
|
|
|
|
PRINT_CRIT_ERROR("cmnd %p still on some list?, %x, %x, %x, %x, %x, %x, %x",
|
|
cmnd, req->opcode, req->scb[0],
|
|
req->flags, req->itt, be32_to_cpu(req->data_length),
|
|
req->cmd_sn,
|
|
be32_to_cpu((__force __be32)(cmnd->pdu.datasize)));
|
|
|
|
if (unlikely(cmnd->parent_req)) {
|
|
struct iscsi_scsi_cmd_hdr *preq =
|
|
cmnd_hdr(cmnd->parent_req);
|
|
PRINT_CRIT_ERROR("%p %x %u", preq, preq->opcode,
|
|
preq->scb[0]);
|
|
}
|
|
sBUG();
|
|
}
|
|
#endif
|
|
if (cmnd->parent_req || isert_cmnd->is_fake_rx)
|
|
isert_release_tx_pdu(cmnd);
|
|
else
|
|
isert_release_rx_pdu(cmnd);
|
|
|
|
TRACE_EXIT();
|
|
}
|
|
|
|
static void isert_preprocessing_done(struct iscsi_cmnd *req)
|
|
{
|
|
req->scst_state = ISCSI_CMD_STATE_AFTER_PREPROC;
|
|
}
|
|
|
|
static void isert_set_sense_data(struct iscsi_cmnd *rsp,
|
|
const u8 *sense_buf, int sense_len)
|
|
{
|
|
u8 *buf;
|
|
|
|
buf = sg_virt(rsp->sg) + ISER_HDRS_SZ;
|
|
|
|
memcpy(buf, &rsp->sense_hdr, sizeof(rsp->sense_hdr));
|
|
memcpy(&buf[sizeof(rsp->sense_hdr)], sense_buf, sense_len);
|
|
}
|
|
|
|
static void isert_set_req_data(struct iscsi_cmnd *req, struct iscsi_cmnd *rsp)
|
|
{
|
|
memcpy(sg_virt(rsp->sg) + ISER_HDRS_SZ,
|
|
sg_virt(req->sg) + ISER_HDRS_SZ, req->bufflen);
|
|
rsp->bufflen = req->bufflen;
|
|
}
|
|
|
|
static void isert_send_data_rsp(struct iscsi_cmnd *req, u8 *sense,
|
|
int sense_len, u8 status, int is_send_status)
|
|
{
|
|
struct iscsi_cmnd *rsp;
|
|
|
|
TRACE_ENTRY();
|
|
|
|
sBUG_ON(!is_send_status);
|
|
|
|
rsp = create_status_rsp(req, status, sense, sense_len);
|
|
|
|
isert_update_len_sn(rsp);
|
|
|
|
conn_get(rsp->conn);
|
|
if (status != SAM_STAT_CHECK_CONDITION)
|
|
isert_send_data_in(req, rsp);
|
|
else
|
|
isert_pdu_tx(rsp);
|
|
|
|
TRACE_EXIT();
|
|
}
|
|
|
|
static void isert_make_conn_wr_active(struct iscsi_conn *conn)
|
|
{
|
|
isert_process_all_writes(conn);
|
|
}
|
|
|
|
static int isert_conn_activate(struct iscsi_conn *conn)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void isert_free_conn(struct iscsi_conn *conn)
|
|
{
|
|
isert_free_connection(conn);
|
|
}
|
|
|
|
void isert_handle_close_connection(struct iscsi_conn *conn)
|
|
{
|
|
isert_mark_conn_closed(conn, 0);
|
|
/*
|
|
* Take care of case where our connection is being closed without
|
|
* being connected to a session - if connection allocation failed for
|
|
* some reason.
|
|
*/
|
|
if (unlikely(!conn->session))
|
|
isert_free_connection(conn);
|
|
else
|
|
start_close_conn(conn);
|
|
}
|
|
|
|
int isert_pdu_rx(struct iscsi_cmnd *cmnd)
|
|
{
|
|
int res = 0;
|
|
scst_data_direction dir;
|
|
|
|
TRACE_ENTRY();
|
|
|
|
#ifdef CONFIG_SCST_EXTRACHECKS
|
|
cmnd->conn->rd_task = current;
|
|
#endif
|
|
iscsi_cmnd_init(cmnd->conn, cmnd, NULL);
|
|
cmnd_rx_start(cmnd);
|
|
|
|
if (unlikely(!cmnd->scst_cmd)) {
|
|
cmnd_rx_end(cmnd);
|
|
goto out;
|
|
}
|
|
|
|
if (unlikely(scst_cmd_prelim_completed(cmnd->scst_cmd) ||
|
|
unlikely(cmnd->prelim_compl_flags != 0))) {
|
|
set_bit(ISCSI_CMD_PRELIM_COMPLETED, &cmnd->prelim_compl_flags);
|
|
cmnd_rx_end(cmnd);
|
|
goto out;
|
|
}
|
|
|
|
dir = scst_cmd_get_data_direction(cmnd->scst_cmd);
|
|
|
|
if (dir & SCST_DATA_WRITE) {
|
|
res = iscsi_cmnd_set_write_buf(cmnd);
|
|
if (unlikely(res))
|
|
goto out;
|
|
res = isert_request_data_out(cmnd);
|
|
cmnd->r2t_len_to_receive = 0;
|
|
cmnd->r2t_len_to_send = 0;
|
|
cmnd->outstanding_r2t = 0;
|
|
} else {
|
|
cmnd_rx_end(cmnd);
|
|
}
|
|
|
|
out:
|
|
TRACE_EXIT_RES(res);
|
|
return res;
|
|
}
|
|
|
|
int isert_data_out_ready(struct iscsi_cmnd *cmnd)
|
|
{
|
|
int res = 0;
|
|
|
|
TRACE_ENTRY();
|
|
#ifdef CONFIG_SCST_EXTRACHECKS
|
|
cmnd->conn->rd_task = current;
|
|
#endif
|
|
cmnd_rx_end(cmnd);
|
|
|
|
TRACE_EXIT_RES(res);
|
|
return res;
|
|
}
|
|
|
|
int isert_data_in_sent(struct iscsi_cmnd *din)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void isert_pdu_err(struct iscsi_cmnd *pdu)
|
|
{
|
|
struct iscsi_conn *conn = pdu->conn;
|
|
|
|
if (!conn->session) /* we are still in login phase */
|
|
return;
|
|
|
|
if (pdu->parent_req) {
|
|
rsp_cmnd_release(pdu);
|
|
conn_put(conn);
|
|
} else {
|
|
/*
|
|
* we will get multiple pdu errors
|
|
* for same PDU with multiple RDMAs case
|
|
*/
|
|
if (pdu->on_write_timeout_list)
|
|
req_cmnd_release_force(pdu);
|
|
}
|
|
}
|
|
|
|
int isert_pdu_sent(struct iscsi_cmnd *pdu)
|
|
{
|
|
struct iscsi_conn *conn = pdu->conn;
|
|
int res = 0;
|
|
|
|
TRACE_ENTRY();
|
|
|
|
if (unlikely(pdu->should_close_conn)) {
|
|
if (pdu->should_close_all_conn) {
|
|
struct iscsi_target *target =
|
|
pdu->conn->session->target;
|
|
|
|
PRINT_INFO("Closing all connections for target %x at initiator's %s request",
|
|
target->tid, conn->session->initiator_name);
|
|
mutex_lock(&target->target_mutex);
|
|
target_del_all_sess(target, 0);
|
|
mutex_unlock(&target->target_mutex);
|
|
} else {
|
|
PRINT_INFO("Closing connection %p at initiator's %s request",
|
|
conn, conn->session->initiator_name);
|
|
mark_conn_closed(conn);
|
|
}
|
|
}
|
|
|
|
/* we may get NULL parent req for login response */
|
|
if (likely(pdu->parent_req)) {
|
|
rsp_cmnd_release(pdu);
|
|
conn_put(conn);
|
|
}
|
|
|
|
TRACE_EXIT_RES(res);
|
|
return res;
|
|
}
|
|
|
|
static ssize_t isert_get_initiator_ip(struct iscsi_conn *conn,
|
|
char *buf, int size)
|
|
{
|
|
int pos;
|
|
struct sockaddr_storage ss;
|
|
size_t addr_len;
|
|
|
|
TRACE_ENTRY();
|
|
|
|
isert_get_peer_addr(conn, (struct sockaddr *)&ss, &addr_len);
|
|
|
|
switch (ss.ss_family) {
|
|
case AF_INET:
|
|
pos = scnprintf(buf, size,
|
|
"%pI4", &((struct sockaddr_in *)&ss)->sin_addr.s_addr);
|
|
break;
|
|
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
|
case AF_INET6:
|
|
pos = scnprintf(buf, size, "[%pI6]",
|
|
&((struct sockaddr_in6 *)&ss)->sin6_addr);
|
|
break;
|
|
#endif
|
|
default:
|
|
pos = scnprintf(buf, size, "Unknown family %d",
|
|
ss.ss_family);
|
|
break;
|
|
}
|
|
|
|
TRACE_EXIT_RES(pos);
|
|
return pos;
|
|
}
|
|
|
|
static struct iscsit_transport isert_transport = {
|
|
.owner = THIS_MODULE,
|
|
.name = "iSER",
|
|
.transport_type = ISCSI_RDMA,
|
|
.iscsit_conn_alloc = isert_conn_alloc,
|
|
.iscsit_conn_activate = isert_conn_activate,
|
|
.iscsit_conn_free = isert_free_conn,
|
|
.iscsit_alloc_cmd = isert_cmnd_alloc,
|
|
.iscsit_free_cmd = isert_cmnd_free,
|
|
.iscsit_preprocessing_done = isert_preprocessing_done,
|
|
.iscsit_send_data_rsp = isert_send_data_rsp,
|
|
.iscsit_make_conn_wr_active = isert_make_conn_wr_active,
|
|
.iscsit_get_initiator_ip = isert_get_initiator_ip,
|
|
.iscsit_send_locally = isert_send_locally,
|
|
.iscsit_mark_conn_closed = isert_mark_conn_closed,
|
|
.iscsit_conn_close = isert_close_conn,
|
|
.iscsit_set_sense_data = isert_set_sense_data,
|
|
.iscsit_set_req_data = isert_set_req_data,
|
|
.iscsit_receive_cmnd_data = isert_receive_cmnd_data,
|
|
.iscsit_close_all_portals = isert_close_all_portals,
|
|
};
|
|
|
|
static void isert_cleanup_module(void)
|
|
{
|
|
iscsit_unreg_transport(&isert_transport);
|
|
isert_cleanup_login_devs();
|
|
}
|
|
|
|
static int __init isert_init_module(void)
|
|
{
|
|
int ret;
|
|
|
|
if (isert_nr_devs > 999) {
|
|
PRINT_ERROR("Invalid argument for isert_nr_devs provided: %d",
|
|
isert_nr_devs);
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
ret = iscsit_reg_transport(&isert_transport);
|
|
if (unlikely(ret))
|
|
goto out;
|
|
|
|
ret = isert_init_login_devs(isert_nr_devs);
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
MODULE_AUTHOR("Yan Burman");
|
|
MODULE_LICENSE("Dual BSD/GPL");
|
|
MODULE_IMPORT_NS(SCST);
|
|
#define DRV_VERSION "3.7.0-pre" "#" __stringify(OFED_FLAVOR)
|
|
#define DRV_RELDATE "11 January 2022"
|
|
MODULE_DESCRIPTION("iSER target transport driver "
|
|
"v" DRV_VERSION " (" DRV_RELDATE ")");
|
|
module_init(isert_init_module);
|
|
module_exit(isert_cleanup_module);
|