Merge branch 'for-next-merge' of git://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending into vhost-net-next
This commit is contained in:
commit
bc7562355f
39 changed files with 4477 additions and 1009 deletions
|
@ -59,5 +59,6 @@ source "drivers/infiniband/ulp/srp/Kconfig"
|
|||
source "drivers/infiniband/ulp/srpt/Kconfig"
|
||||
|
||||
source "drivers/infiniband/ulp/iser/Kconfig"
|
||||
source "drivers/infiniband/ulp/isert/Kconfig"
|
||||
|
||||
endif # INFINIBAND
|
||||
|
|
|
@ -13,3 +13,4 @@ obj-$(CONFIG_INFINIBAND_IPOIB) += ulp/ipoib/
|
|||
obj-$(CONFIG_INFINIBAND_SRP) += ulp/srp/
|
||||
obj-$(CONFIG_INFINIBAND_SRPT) += ulp/srpt/
|
||||
obj-$(CONFIG_INFINIBAND_ISER) += ulp/iser/
|
||||
obj-$(CONFIG_INFINIBAND_ISERT) += ulp/isert/
|
||||
|
|
5
drivers/infiniband/ulp/isert/Kconfig
Normal file
5
drivers/infiniband/ulp/isert/Kconfig
Normal file
|
@ -0,0 +1,5 @@
|
|||
config INFINIBAND_ISERT
|
||||
tristate "iSCSI Extentions for RDMA (iSER) target support"
|
||||
depends on INET && INFINIBAND_ADDR_TRANS && TARGET_CORE && ISCSI_TARGET
|
||||
---help---
|
||||
Support for iSCSI Extentions for RDMA (iSER) Target on Infiniband fabrics.
|
2
drivers/infiniband/ulp/isert/Makefile
Normal file
2
drivers/infiniband/ulp/isert/Makefile
Normal file
|
@ -0,0 +1,2 @@
|
|||
ccflags-y := -Idrivers/target -Idrivers/target/iscsi
|
||||
obj-$(CONFIG_INFINIBAND_ISERT) += ib_isert.o
|
2281
drivers/infiniband/ulp/isert/ib_isert.c
Normal file
2281
drivers/infiniband/ulp/isert/ib_isert.c
Normal file
File diff suppressed because it is too large
Load diff
138
drivers/infiniband/ulp/isert/ib_isert.h
Normal file
138
drivers/infiniband/ulp/isert/ib_isert.h
Normal file
|
@ -0,0 +1,138 @@
|
|||
#include <linux/socket.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/in6.h>
|
||||
#include <rdma/ib_verbs.h>
|
||||
#include <rdma/rdma_cm.h>
|
||||
|
||||
#define ISERT_RDMA_LISTEN_BACKLOG 10
|
||||
|
||||
enum isert_desc_type {
|
||||
ISCSI_TX_CONTROL,
|
||||
ISCSI_TX_DATAIN
|
||||
};
|
||||
|
||||
enum iser_ib_op_code {
|
||||
ISER_IB_RECV,
|
||||
ISER_IB_SEND,
|
||||
ISER_IB_RDMA_WRITE,
|
||||
ISER_IB_RDMA_READ,
|
||||
};
|
||||
|
||||
enum iser_conn_state {
|
||||
ISER_CONN_INIT,
|
||||
ISER_CONN_UP,
|
||||
ISER_CONN_TERMINATING,
|
||||
ISER_CONN_DOWN,
|
||||
};
|
||||
|
||||
struct iser_rx_desc {
|
||||
struct iser_hdr iser_header;
|
||||
struct iscsi_hdr iscsi_header;
|
||||
char data[ISER_RECV_DATA_SEG_LEN];
|
||||
u64 dma_addr;
|
||||
struct ib_sge rx_sg;
|
||||
char pad[ISER_RX_PAD_SIZE];
|
||||
} __packed;
|
||||
|
||||
struct iser_tx_desc {
|
||||
struct iser_hdr iser_header;
|
||||
struct iscsi_hdr iscsi_header;
|
||||
enum isert_desc_type type;
|
||||
u64 dma_addr;
|
||||
struct ib_sge tx_sg[2];
|
||||
int num_sge;
|
||||
struct isert_cmd *isert_cmd;
|
||||
struct ib_send_wr send_wr;
|
||||
} __packed;
|
||||
|
||||
struct isert_rdma_wr {
|
||||
struct list_head wr_list;
|
||||
struct isert_cmd *isert_cmd;
|
||||
enum iser_ib_op_code iser_ib_op;
|
||||
struct ib_sge *ib_sge;
|
||||
int num_sge;
|
||||
struct scatterlist *sge;
|
||||
int send_wr_num;
|
||||
struct ib_send_wr *send_wr;
|
||||
};
|
||||
|
||||
struct isert_cmd {
|
||||
uint32_t read_stag;
|
||||
uint32_t write_stag;
|
||||
uint64_t read_va;
|
||||
uint64_t write_va;
|
||||
u64 sense_buf_dma;
|
||||
u32 sense_buf_len;
|
||||
u32 read_va_off;
|
||||
u32 write_va_off;
|
||||
u32 rdma_wr_num;
|
||||
struct isert_conn *conn;
|
||||
struct iscsi_cmd iscsi_cmd;
|
||||
struct ib_sge *ib_sge;
|
||||
struct iser_tx_desc tx_desc;
|
||||
struct isert_rdma_wr rdma_wr;
|
||||
struct work_struct comp_work;
|
||||
};
|
||||
|
||||
struct isert_device;
|
||||
|
||||
struct isert_conn {
|
||||
enum iser_conn_state state;
|
||||
bool logout_posted;
|
||||
int post_recv_buf_count;
|
||||
atomic_t post_send_buf_count;
|
||||
u32 responder_resources;
|
||||
u32 initiator_depth;
|
||||
u32 max_sge;
|
||||
char *login_buf;
|
||||
char *login_req_buf;
|
||||
char *login_rsp_buf;
|
||||
u64 login_req_dma;
|
||||
u64 login_rsp_dma;
|
||||
unsigned int conn_rx_desc_head;
|
||||
struct iser_rx_desc *conn_rx_descs;
|
||||
struct ib_recv_wr conn_rx_wr[ISERT_MIN_POSTED_RX];
|
||||
struct iscsi_conn *conn;
|
||||
struct list_head conn_accept_node;
|
||||
struct completion conn_login_comp;
|
||||
struct iser_tx_desc conn_login_tx_desc;
|
||||
struct rdma_cm_id *conn_cm_id;
|
||||
struct ib_pd *conn_pd;
|
||||
struct ib_mr *conn_mr;
|
||||
struct ib_qp *conn_qp;
|
||||
struct isert_device *conn_device;
|
||||
struct work_struct conn_logout_work;
|
||||
wait_queue_head_t conn_wait;
|
||||
wait_queue_head_t conn_wait_comp_err;
|
||||
struct kref conn_kref;
|
||||
};
|
||||
|
||||
#define ISERT_MAX_CQ 64
|
||||
|
||||
struct isert_cq_desc {
|
||||
struct isert_device *device;
|
||||
int cq_index;
|
||||
struct work_struct cq_rx_work;
|
||||
struct work_struct cq_tx_work;
|
||||
};
|
||||
|
||||
struct isert_device {
|
||||
int cqs_used;
|
||||
int refcount;
|
||||
int cq_active_qps[ISERT_MAX_CQ];
|
||||
struct ib_device *ib_device;
|
||||
struct ib_pd *dev_pd;
|
||||
struct ib_mr *dev_mr;
|
||||
struct ib_cq *dev_rx_cq[ISERT_MAX_CQ];
|
||||
struct ib_cq *dev_tx_cq[ISERT_MAX_CQ];
|
||||
struct isert_cq_desc *cq_desc;
|
||||
struct list_head dev_node;
|
||||
};
|
||||
|
||||
struct isert_np {
|
||||
wait_queue_head_t np_accept_wq;
|
||||
struct rdma_cm_id *np_cm_id;
|
||||
struct mutex np_accept_mutex;
|
||||
struct list_head np_accept_list;
|
||||
struct completion np_login_comp;
|
||||
};
|
47
drivers/infiniband/ulp/isert/isert_proto.h
Normal file
47
drivers/infiniband/ulp/isert/isert_proto.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/* From iscsi_iser.h */
|
||||
|
||||
struct iser_hdr {
|
||||
u8 flags;
|
||||
u8 rsvd[3];
|
||||
__be32 write_stag; /* write rkey */
|
||||
__be64 write_va;
|
||||
__be32 read_stag; /* read rkey */
|
||||
__be64 read_va;
|
||||
} __packed;
|
||||
|
||||
/*Constant PDU lengths calculations */
|
||||
#define ISER_HEADERS_LEN (sizeof(struct iser_hdr) + sizeof(struct iscsi_hdr))
|
||||
|
||||
#define ISER_RECV_DATA_SEG_LEN 8192
|
||||
#define ISER_RX_PAYLOAD_SIZE (ISER_HEADERS_LEN + ISER_RECV_DATA_SEG_LEN)
|
||||
#define ISER_RX_LOGIN_SIZE (ISER_HEADERS_LEN + ISCSI_DEF_MAX_RECV_SEG_LEN)
|
||||
|
||||
/* QP settings */
|
||||
/* Maximal bounds on received asynchronous PDUs */
|
||||
#define ISERT_MAX_TX_MISC_PDUS 4 /* NOOP_IN(2) , ASYNC_EVENT(2) */
|
||||
|
||||
#define ISERT_MAX_RX_MISC_PDUS 6 /* NOOP_OUT(2), TEXT(1), *
|
||||
* SCSI_TMFUNC(2), LOGOUT(1) */
|
||||
|
||||
#define ISCSI_DEF_XMIT_CMDS_MAX 128 /* from libiscsi.h, must be power of 2 */
|
||||
|
||||
#define ISERT_QP_MAX_RECV_DTOS (ISCSI_DEF_XMIT_CMDS_MAX)
|
||||
|
||||
#define ISERT_MIN_POSTED_RX (ISCSI_DEF_XMIT_CMDS_MAX >> 2)
|
||||
|
||||
#define ISERT_INFLIGHT_DATAOUTS 8
|
||||
|
||||
#define ISERT_QP_MAX_REQ_DTOS (ISCSI_DEF_XMIT_CMDS_MAX * \
|
||||
(1 + ISERT_INFLIGHT_DATAOUTS) + \
|
||||
ISERT_MAX_TX_MISC_PDUS + \
|
||||
ISERT_MAX_RX_MISC_PDUS)
|
||||
|
||||
#define ISER_RX_PAD_SIZE (ISER_RECV_DATA_SEG_LEN + 4096 - \
|
||||
(ISER_RX_PAYLOAD_SIZE + sizeof(u64) + sizeof(struct ib_sge)))
|
||||
|
||||
#define ISER_VER 0x10
|
||||
#define ISER_WSV 0x08
|
||||
#define ISER_RSV 0x04
|
||||
#define ISCSI_CTRL 0x10
|
||||
#define ISER_HELLO 0x20
|
||||
#define ISER_HELLORPLY 0x30
|
|
@ -2585,25 +2585,6 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle,
|
|||
ha->tgt.tgt_ops->free_cmd(cmd);
|
||||
}
|
||||
|
||||
/* ha->hardware_lock supposed to be held on entry */
|
||||
/* called via callback from qla2xxx */
|
||||
void qlt_ctio_completion(struct scsi_qla_host *vha, uint32_t handle)
|
||||
{
|
||||
struct qla_hw_data *ha = vha->hw;
|
||||
struct qla_tgt *tgt = ha->tgt.qla_tgt;
|
||||
|
||||
if (likely(tgt == NULL)) {
|
||||
ql_dbg(ql_dbg_tgt, vha, 0xe021,
|
||||
"CTIO, but target mode not enabled"
|
||||
" (ha %d %p handle %#x)", vha->vp_idx, ha, handle);
|
||||
return;
|
||||
}
|
||||
|
||||
tgt->irq_cmd_count++;
|
||||
qlt_do_ctio_completion(vha, handle, CTIO_SUCCESS, NULL);
|
||||
tgt->irq_cmd_count--;
|
||||
}
|
||||
|
||||
static inline int qlt_get_fcp_task_attr(struct scsi_qla_host *vha,
|
||||
uint8_t task_codes)
|
||||
{
|
||||
|
|
|
@ -980,7 +980,6 @@ extern int qlt_xmit_response(struct qla_tgt_cmd *, int, uint8_t);
|
|||
extern void qlt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *);
|
||||
extern void qlt_free_mcmd(struct qla_tgt_mgmt_cmd *);
|
||||
extern void qlt_free_cmd(struct qla_tgt_cmd *cmd);
|
||||
extern void qlt_ctio_completion(struct scsi_qla_host *, uint32_t);
|
||||
extern void qlt_async_event(uint16_t, struct scsi_qla_host *, uint16_t *);
|
||||
extern void qlt_enable_vha(struct scsi_qla_host *);
|
||||
extern void qlt_vport_create(struct scsi_qla_host *, struct qla_hw_data *);
|
||||
|
|
|
@ -15,6 +15,7 @@ iscsi_target_mod-y += iscsi_target_parameters.o \
|
|||
iscsi_target_util.o \
|
||||
iscsi_target.o \
|
||||
iscsi_target_configfs.o \
|
||||
iscsi_target_stat.o
|
||||
iscsi_target_stat.o \
|
||||
iscsi_target_transport.o
|
||||
|
||||
obj-$(CONFIG_ISCSI_TARGET) += iscsi_target_mod.o
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -16,11 +16,12 @@ extern int iscsit_reset_np_thread(struct iscsi_np *, struct iscsi_tpg_np *,
|
|||
struct iscsi_portal_group *);
|
||||
extern int iscsit_del_np(struct iscsi_np *);
|
||||
extern int iscsit_add_reject_from_cmd(u8, int, int, unsigned char *, struct iscsi_cmd *);
|
||||
extern void iscsit_set_unsoliticed_dataout(struct iscsi_cmd *);
|
||||
extern int iscsit_logout_closesession(struct iscsi_cmd *, struct iscsi_conn *);
|
||||
extern int iscsit_logout_closeconnection(struct iscsi_cmd *, struct iscsi_conn *);
|
||||
extern int iscsit_logout_removeconnforrecovery(struct iscsi_cmd *, struct iscsi_conn *);
|
||||
extern int iscsit_send_async_msg(struct iscsi_conn *, u16, u8, u8);
|
||||
extern int iscsit_build_r2ts_for_cmd(struct iscsi_cmd *, struct iscsi_conn *, bool recovery);
|
||||
extern int iscsit_build_r2ts_for_cmd(struct iscsi_conn *, struct iscsi_cmd *, bool recovery);
|
||||
extern void iscsit_thread_get_cpumask(struct iscsi_conn *);
|
||||
extern int iscsi_target_tx_thread(void *);
|
||||
extern int iscsi_target_rx_thread(void *);
|
||||
|
|
|
@ -49,32 +49,6 @@ static void chap_binaryhex_to_asciihex(char *dst, char *src, int src_len)
|
|||
}
|
||||
}
|
||||
|
||||
static void chap_set_random(char *data, int length)
|
||||
{
|
||||
long r;
|
||||
unsigned n;
|
||||
|
||||
while (length > 0) {
|
||||
get_random_bytes(&r, sizeof(long));
|
||||
r = r ^ (r >> 8);
|
||||
r = r ^ (r >> 4);
|
||||
n = r & 0x7;
|
||||
|
||||
get_random_bytes(&r, sizeof(long));
|
||||
r = r ^ (r >> 8);
|
||||
r = r ^ (r >> 5);
|
||||
n = (n << 3) | (r & 0x7);
|
||||
|
||||
get_random_bytes(&r, sizeof(long));
|
||||
r = r ^ (r >> 8);
|
||||
r = r ^ (r >> 5);
|
||||
n = (n << 2) | (r & 0x3);
|
||||
|
||||
*data++ = n;
|
||||
length--;
|
||||
}
|
||||
}
|
||||
|
||||
static void chap_gen_challenge(
|
||||
struct iscsi_conn *conn,
|
||||
int caller,
|
||||
|
@ -86,7 +60,7 @@ static void chap_gen_challenge(
|
|||
|
||||
memset(challenge_asciihex, 0, CHAP_CHALLENGE_LENGTH * 2 + 1);
|
||||
|
||||
chap_set_random(chap->challenge, CHAP_CHALLENGE_LENGTH);
|
||||
get_random_bytes(chap->challenge, CHAP_CHALLENGE_LENGTH);
|
||||
chap_binaryhex_to_asciihex(challenge_asciihex, chap->challenge,
|
||||
CHAP_CHALLENGE_LENGTH);
|
||||
/*
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <target/target_core_fabric_configfs.h>
|
||||
#include <target/target_core_configfs.h>
|
||||
#include <target/configfs_macros.h>
|
||||
#include <target/iscsi/iscsi_transport.h>
|
||||
|
||||
#include "iscsi_target_core.h"
|
||||
#include "iscsi_target_parameters.h"
|
||||
|
@ -124,8 +125,87 @@ static ssize_t lio_target_np_store_sctp(
|
|||
|
||||
TF_NP_BASE_ATTR(lio_target, sctp, S_IRUGO | S_IWUSR);
|
||||
|
||||
static ssize_t lio_target_np_show_iser(
|
||||
struct se_tpg_np *se_tpg_np,
|
||||
char *page)
|
||||
{
|
||||
struct iscsi_tpg_np *tpg_np = container_of(se_tpg_np,
|
||||
struct iscsi_tpg_np, se_tpg_np);
|
||||
struct iscsi_tpg_np *tpg_np_iser;
|
||||
ssize_t rb;
|
||||
|
||||
tpg_np_iser = iscsit_tpg_locate_child_np(tpg_np, ISCSI_INFINIBAND);
|
||||
if (tpg_np_iser)
|
||||
rb = sprintf(page, "1\n");
|
||||
else
|
||||
rb = sprintf(page, "0\n");
|
||||
|
||||
return rb;
|
||||
}
|
||||
|
||||
static ssize_t lio_target_np_store_iser(
|
||||
struct se_tpg_np *se_tpg_np,
|
||||
const char *page,
|
||||
size_t count)
|
||||
{
|
||||
struct iscsi_np *np;
|
||||
struct iscsi_portal_group *tpg;
|
||||
struct iscsi_tpg_np *tpg_np = container_of(se_tpg_np,
|
||||
struct iscsi_tpg_np, se_tpg_np);
|
||||
struct iscsi_tpg_np *tpg_np_iser = NULL;
|
||||
char *endptr;
|
||||
u32 op;
|
||||
int rc;
|
||||
|
||||
op = simple_strtoul(page, &endptr, 0);
|
||||
if ((op != 1) && (op != 0)) {
|
||||
pr_err("Illegal value for tpg_enable: %u\n", op);
|
||||
return -EINVAL;
|
||||
}
|
||||
np = tpg_np->tpg_np;
|
||||
if (!np) {
|
||||
pr_err("Unable to locate struct iscsi_np from"
|
||||
" struct iscsi_tpg_np\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tpg = tpg_np->tpg;
|
||||
if (iscsit_get_tpg(tpg) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (op) {
|
||||
int rc = request_module("ib_isert");
|
||||
if (rc != 0)
|
||||
pr_warn("Unable to request_module for ib_isert\n");
|
||||
|
||||
tpg_np_iser = iscsit_tpg_add_network_portal(tpg, &np->np_sockaddr,
|
||||
np->np_ip, tpg_np, ISCSI_INFINIBAND);
|
||||
if (!tpg_np_iser || IS_ERR(tpg_np_iser))
|
||||
goto out;
|
||||
} else {
|
||||
tpg_np_iser = iscsit_tpg_locate_child_np(tpg_np, ISCSI_INFINIBAND);
|
||||
if (!tpg_np_iser)
|
||||
goto out;
|
||||
|
||||
rc = iscsit_tpg_del_network_portal(tpg, tpg_np_iser);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
printk("lio_target_np_store_iser() done, op: %d\n", op);
|
||||
|
||||
iscsit_put_tpg(tpg);
|
||||
return count;
|
||||
out:
|
||||
iscsit_put_tpg(tpg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
TF_NP_BASE_ATTR(lio_target, iser, S_IRUGO | S_IWUSR);
|
||||
|
||||
static struct configfs_attribute *lio_target_portal_attrs[] = {
|
||||
&lio_target_np_sctp.attr,
|
||||
&lio_target_np_iser.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -1536,16 +1616,18 @@ static int lio_queue_data_in(struct se_cmd *se_cmd)
|
|||
struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
|
||||
|
||||
cmd->i_state = ISTATE_SEND_DATAIN;
|
||||
iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state);
|
||||
cmd->conn->conn_transport->iscsit_queue_data_in(cmd->conn, cmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lio_write_pending(struct se_cmd *se_cmd)
|
||||
{
|
||||
struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
|
||||
struct iscsi_conn *conn = cmd->conn;
|
||||
|
||||
if (!cmd->immediate_data && !cmd->unsolicited_data)
|
||||
return iscsit_build_r2ts_for_cmd(cmd, cmd->conn, false);
|
||||
return conn->conn_transport->iscsit_get_dataout(conn, cmd, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1567,7 +1649,8 @@ static int lio_queue_status(struct se_cmd *se_cmd)
|
|||
struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
|
||||
|
||||
cmd->i_state = ISTATE_SEND_STATUS;
|
||||
iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state);
|
||||
cmd->conn->conn_transport->iscsit_queue_status(cmd->conn, cmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1696,11 +1779,17 @@ static void lio_set_default_node_attributes(struct se_node_acl *se_acl)
|
|||
iscsit_set_default_node_attribues(acl);
|
||||
}
|
||||
|
||||
static int lio_check_stop_free(struct se_cmd *se_cmd)
|
||||
{
|
||||
return target_put_sess_cmd(se_cmd->se_sess, se_cmd);
|
||||
}
|
||||
|
||||
static void lio_release_cmd(struct se_cmd *se_cmd)
|
||||
{
|
||||
struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
|
||||
|
||||
iscsit_release_cmd(cmd);
|
||||
pr_debug("Entering lio_release_cmd for se_cmd: %p\n", se_cmd);
|
||||
cmd->release_cmd(cmd);
|
||||
}
|
||||
|
||||
/* End functions for target_core_fabric_ops */
|
||||
|
@ -1740,6 +1829,7 @@ int iscsi_target_register_configfs(void)
|
|||
fabric->tf_ops.tpg_alloc_fabric_acl = &lio_tpg_alloc_fabric_acl;
|
||||
fabric->tf_ops.tpg_release_fabric_acl = &lio_tpg_release_fabric_acl;
|
||||
fabric->tf_ops.tpg_get_inst_index = &lio_tpg_get_inst_index;
|
||||
fabric->tf_ops.check_stop_free = &lio_check_stop_free,
|
||||
fabric->tf_ops.release_cmd = &lio_release_cmd;
|
||||
fabric->tf_ops.shutdown_session = &lio_tpg_shutdown_session;
|
||||
fabric->tf_ops.close_session = &lio_tpg_close_session;
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
|
||||
#define ISCSI_IOV_DATA_BUFFER 5
|
||||
|
||||
enum tpg_np_network_transport_table {
|
||||
enum iscsit_transport_type {
|
||||
ISCSI_TCP = 0,
|
||||
ISCSI_SCTP_TCP = 1,
|
||||
ISCSI_SCTP_UDP = 2,
|
||||
|
@ -244,6 +244,11 @@ struct iscsi_conn_ops {
|
|||
u8 IFMarker; /* [0,1] == [No,Yes] */
|
||||
u32 OFMarkInt; /* [1..65535] */
|
||||
u32 IFMarkInt; /* [1..65535] */
|
||||
/*
|
||||
* iSER specific connection parameters
|
||||
*/
|
||||
u32 InitiatorRecvDataSegmentLength; /* [512..2**24-1] */
|
||||
u32 TargetRecvDataSegmentLength; /* [512..2**24-1] */
|
||||
};
|
||||
|
||||
struct iscsi_sess_ops {
|
||||
|
@ -265,6 +270,10 @@ struct iscsi_sess_ops {
|
|||
u8 DataSequenceInOrder; /* [0,1] == [No,Yes] */
|
||||
u8 ErrorRecoveryLevel; /* [0..2] */
|
||||
u8 SessionType; /* [0,1] == [Normal,Discovery]*/
|
||||
/*
|
||||
* iSER specific session parameters
|
||||
*/
|
||||
u8 RDMAExtensions; /* [0,1] == [No,Yes] */
|
||||
};
|
||||
|
||||
struct iscsi_queue_req {
|
||||
|
@ -284,6 +293,7 @@ struct iscsi_data_count {
|
|||
};
|
||||
|
||||
struct iscsi_param_list {
|
||||
bool iser;
|
||||
struct list_head param_list;
|
||||
struct list_head extra_response_list;
|
||||
};
|
||||
|
@ -475,6 +485,7 @@ struct iscsi_cmd {
|
|||
u32 first_data_sg_off;
|
||||
u32 kmapped_nents;
|
||||
sense_reason_t sense_reason;
|
||||
void (*release_cmd)(struct iscsi_cmd *);
|
||||
} ____cacheline_aligned;
|
||||
|
||||
struct iscsi_tmr_req {
|
||||
|
@ -503,6 +514,7 @@ struct iscsi_conn {
|
|||
u16 login_port;
|
||||
u16 local_port;
|
||||
int net_size;
|
||||
int login_family;
|
||||
u32 auth_id;
|
||||
u32 conn_flags;
|
||||
/* Used for iscsi_tx_login_rsp() */
|
||||
|
@ -562,9 +574,12 @@ struct iscsi_conn {
|
|||
struct list_head immed_queue_list;
|
||||
struct list_head response_queue_list;
|
||||
struct iscsi_conn_ops *conn_ops;
|
||||
struct iscsi_login *conn_login;
|
||||
struct iscsit_transport *conn_transport;
|
||||
struct iscsi_param_list *param_list;
|
||||
/* Used for per connection auth state machine */
|
||||
void *auth_protocol;
|
||||
void *context;
|
||||
struct iscsi_login_thread_s *login_thread;
|
||||
struct iscsi_portal_group *tpg;
|
||||
/* Pointer to parent session */
|
||||
|
@ -663,6 +678,8 @@ struct iscsi_login {
|
|||
u8 first_request;
|
||||
u8 version_min;
|
||||
u8 version_max;
|
||||
u8 login_complete;
|
||||
u8 login_failed;
|
||||
char isid[6];
|
||||
u32 cmd_sn;
|
||||
itt_t init_task_tag;
|
||||
|
@ -670,10 +687,11 @@ struct iscsi_login {
|
|||
u32 rsp_length;
|
||||
u16 cid;
|
||||
u16 tsih;
|
||||
char *req;
|
||||
char *rsp;
|
||||
char req[ISCSI_HDR_LEN];
|
||||
char rsp[ISCSI_HDR_LEN];
|
||||
char *req_buf;
|
||||
char *rsp_buf;
|
||||
struct iscsi_conn *conn;
|
||||
} ____cacheline_aligned;
|
||||
|
||||
struct iscsi_node_attrib {
|
||||
|
@ -754,6 +772,8 @@ struct iscsi_np {
|
|||
struct task_struct *np_thread;
|
||||
struct timer_list np_login_timer;
|
||||
struct iscsi_portal_group *np_login_tpg;
|
||||
void *np_context;
|
||||
struct iscsit_transport *np_transport;
|
||||
struct list_head np_list;
|
||||
} ____cacheline_aligned;
|
||||
|
||||
|
|
|
@ -60,8 +60,13 @@ void iscsit_increment_maxcmdsn(struct iscsi_cmd *cmd, struct iscsi_session *sess
|
|||
|
||||
cmd->maxcmdsn_inc = 1;
|
||||
|
||||
mutex_lock(&sess->cmdsn_mutex);
|
||||
if (!mutex_trylock(&sess->cmdsn_mutex)) {
|
||||
sess->max_cmd_sn += 1;
|
||||
pr_debug("Updated MaxCmdSN to 0x%08x\n", sess->max_cmd_sn);
|
||||
return;
|
||||
}
|
||||
sess->max_cmd_sn += 1;
|
||||
pr_debug("Updated MaxCmdSN to 0x%08x\n", sess->max_cmd_sn);
|
||||
mutex_unlock(&sess->cmdsn_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(iscsit_increment_maxcmdsn);
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <scsi/iscsi_proto.h>
|
||||
#include <target/target_core_base.h>
|
||||
#include <target/target_core_fabric.h>
|
||||
#include <target/iscsi/iscsi_transport.h>
|
||||
|
||||
#include "iscsi_target_core.h"
|
||||
#include "iscsi_target_seq_pdu_list.h"
|
||||
|
@ -53,6 +54,9 @@ int iscsit_dump_data_payload(
|
|||
u32 length, padding, offset = 0, size;
|
||||
struct kvec iov;
|
||||
|
||||
if (conn->sess->sess_ops->RDMAExtensions)
|
||||
return 0;
|
||||
|
||||
length = (buf_len > OFFLOAD_BUF_SIZE) ? OFFLOAD_BUF_SIZE : buf_len;
|
||||
|
||||
buf = kzalloc(length, GFP_ATOMIC);
|
||||
|
@ -919,6 +923,7 @@ int iscsit_execute_ooo_cmdsns(struct iscsi_session *sess)
|
|||
int iscsit_execute_cmd(struct iscsi_cmd *cmd, int ooo)
|
||||
{
|
||||
struct se_cmd *se_cmd = &cmd->se_cmd;
|
||||
struct iscsi_conn *conn = cmd->conn;
|
||||
int lr = 0;
|
||||
|
||||
spin_lock_bh(&cmd->istate_lock);
|
||||
|
@ -981,7 +986,7 @@ int iscsit_execute_cmd(struct iscsi_cmd *cmd, int ooo)
|
|||
return 0;
|
||||
|
||||
iscsit_set_dataout_sequence_values(cmd);
|
||||
iscsit_build_r2ts_for_cmd(cmd, cmd->conn, false);
|
||||
conn->conn_transport->iscsit_get_dataout(conn, cmd, false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -999,10 +1004,7 @@ int iscsit_execute_cmd(struct iscsi_cmd *cmd, int ooo)
|
|||
if (transport_check_aborted_status(se_cmd, 1) != 0)
|
||||
return 0;
|
||||
|
||||
iscsit_set_dataout_sequence_values(cmd);
|
||||
spin_lock_bh(&cmd->dataout_timeout_lock);
|
||||
iscsit_start_dataout_timer(cmd, cmd->conn);
|
||||
spin_unlock_bh(&cmd->dataout_timeout_lock);
|
||||
iscsit_set_unsoliticed_dataout(cmd);
|
||||
}
|
||||
return transport_handle_cdb_direct(&cmd->se_cmd);
|
||||
|
||||
|
@ -1290,3 +1292,4 @@ void iscsit_stop_dataout_timer(struct iscsi_cmd *cmd)
|
|||
cmd->init_task_tag);
|
||||
spin_unlock_bh(&cmd->dataout_timeout_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(iscsit_stop_dataout_timer);
|
||||
|
|
|
@ -39,8 +39,39 @@
|
|||
#include "iscsi_target.h"
|
||||
#include "iscsi_target_parameters.h"
|
||||
|
||||
static int iscsi_login_init_conn(struct iscsi_conn *conn)
|
||||
#include <target/iscsi/iscsi_transport.h>
|
||||
|
||||
static struct iscsi_login *iscsi_login_init_conn(struct iscsi_conn *conn)
|
||||
{
|
||||
struct iscsi_login *login;
|
||||
|
||||
login = kzalloc(sizeof(struct iscsi_login), GFP_KERNEL);
|
||||
if (!login) {
|
||||
pr_err("Unable to allocate memory for struct iscsi_login.\n");
|
||||
return NULL;
|
||||
}
|
||||
login->conn = conn;
|
||||
login->first_request = 1;
|
||||
|
||||
login->req_buf = kzalloc(MAX_KEY_VALUE_PAIRS, GFP_KERNEL);
|
||||
if (!login->req_buf) {
|
||||
pr_err("Unable to allocate memory for response buffer.\n");
|
||||
goto out_login;
|
||||
}
|
||||
|
||||
login->rsp_buf = kzalloc(MAX_KEY_VALUE_PAIRS, GFP_KERNEL);
|
||||
if (!login->rsp_buf) {
|
||||
pr_err("Unable to allocate memory for request buffer.\n");
|
||||
goto out_req_buf;
|
||||
}
|
||||
|
||||
conn->conn_ops = kzalloc(sizeof(struct iscsi_conn_ops), GFP_KERNEL);
|
||||
if (!conn->conn_ops) {
|
||||
pr_err("Unable to allocate memory for"
|
||||
" struct iscsi_conn_ops.\n");
|
||||
goto out_rsp_buf;
|
||||
}
|
||||
|
||||
init_waitqueue_head(&conn->queues_wq);
|
||||
INIT_LIST_HEAD(&conn->conn_list);
|
||||
INIT_LIST_HEAD(&conn->conn_cmd_list);
|
||||
|
@ -62,10 +93,21 @@ static int iscsi_login_init_conn(struct iscsi_conn *conn)
|
|||
|
||||
if (!zalloc_cpumask_var(&conn->conn_cpumask, GFP_KERNEL)) {
|
||||
pr_err("Unable to allocate conn->conn_cpumask\n");
|
||||
return -ENOMEM;
|
||||
goto out_conn_ops;
|
||||
}
|
||||
conn->conn_login = login;
|
||||
|
||||
return 0;
|
||||
return login;
|
||||
|
||||
out_conn_ops:
|
||||
kfree(conn->conn_ops);
|
||||
out_rsp_buf:
|
||||
kfree(login->rsp_buf);
|
||||
out_req_buf:
|
||||
kfree(login->req_buf);
|
||||
out_login:
|
||||
kfree(login);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -298,6 +340,7 @@ static int iscsi_login_zero_tsih_s2(
|
|||
struct iscsi_node_attrib *na;
|
||||
struct iscsi_session *sess = conn->sess;
|
||||
unsigned char buf[32];
|
||||
bool iser = false;
|
||||
|
||||
sess->tpg = conn->tpg;
|
||||
|
||||
|
@ -319,7 +362,10 @@ static int iscsi_login_zero_tsih_s2(
|
|||
return -1;
|
||||
}
|
||||
|
||||
iscsi_set_keys_to_negotiate(0, conn->param_list);
|
||||
if (conn->conn_transport->transport_type == ISCSI_INFINIBAND)
|
||||
iser = true;
|
||||
|
||||
iscsi_set_keys_to_negotiate(conn->param_list, iser);
|
||||
|
||||
if (sess->sess_ops->SessionType)
|
||||
return iscsi_set_keys_irrelevant_for_discovery(
|
||||
|
@ -357,6 +403,56 @@ static int iscsi_login_zero_tsih_s2(
|
|||
|
||||
if (iscsi_login_disable_FIM_keys(conn->param_list, conn) < 0)
|
||||
return -1;
|
||||
/*
|
||||
* Set RDMAExtensions=Yes by default for iSER enabled network portals
|
||||
*/
|
||||
if (iser) {
|
||||
struct iscsi_param *param;
|
||||
unsigned long mrdsl, off;
|
||||
int rc;
|
||||
|
||||
sprintf(buf, "RDMAExtensions=Yes");
|
||||
if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) {
|
||||
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
|
||||
ISCSI_LOGIN_STATUS_NO_RESOURCES);
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* Make MaxRecvDataSegmentLength PAGE_SIZE aligned for
|
||||
* Immediate Data + Unsolicitied Data-OUT if necessary..
|
||||
*/
|
||||
param = iscsi_find_param_from_key("MaxRecvDataSegmentLength",
|
||||
conn->param_list);
|
||||
if (!param) {
|
||||
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
|
||||
ISCSI_LOGIN_STATUS_NO_RESOURCES);
|
||||
return -1;
|
||||
}
|
||||
rc = strict_strtoul(param->value, 0, &mrdsl);
|
||||
if (rc < 0) {
|
||||
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
|
||||
ISCSI_LOGIN_STATUS_NO_RESOURCES);
|
||||
return -1;
|
||||
}
|
||||
off = mrdsl % PAGE_SIZE;
|
||||
if (!off)
|
||||
return 0;
|
||||
|
||||
if (mrdsl < PAGE_SIZE)
|
||||
mrdsl = PAGE_SIZE;
|
||||
else
|
||||
mrdsl -= off;
|
||||
|
||||
pr_warn("Aligning ISER MaxRecvDataSegmentLength: %lu down"
|
||||
" to PAGE_SIZE\n", mrdsl);
|
||||
|
||||
sprintf(buf, "MaxRecvDataSegmentLength=%lu\n", mrdsl);
|
||||
if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) {
|
||||
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
|
||||
ISCSI_LOGIN_STATUS_NO_RESOURCES);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -436,6 +532,7 @@ static int iscsi_login_non_zero_tsih_s2(
|
|||
struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
|
||||
struct se_session *se_sess, *se_sess_tmp;
|
||||
struct iscsi_login_req *pdu = (struct iscsi_login_req *)buf;
|
||||
bool iser = false;
|
||||
|
||||
spin_lock_bh(&se_tpg->session_lock);
|
||||
list_for_each_entry_safe(se_sess, se_sess_tmp, &se_tpg->tpg_sess_list,
|
||||
|
@ -485,7 +582,10 @@ static int iscsi_login_non_zero_tsih_s2(
|
|||
return -1;
|
||||
}
|
||||
|
||||
iscsi_set_keys_to_negotiate(0, conn->param_list);
|
||||
if (conn->conn_transport->transport_type == ISCSI_INFINIBAND)
|
||||
iser = true;
|
||||
|
||||
iscsi_set_keys_to_negotiate(conn->param_list, iser);
|
||||
/*
|
||||
* Need to send TargetPortalGroupTag back in first login response
|
||||
* on any iSCSI connection where the Initiator provides TargetName.
|
||||
|
@ -574,6 +674,11 @@ int iscsi_login_post_auth_non_zero_tsih(
|
|||
static void iscsi_post_login_start_timers(struct iscsi_conn *conn)
|
||||
{
|
||||
struct iscsi_session *sess = conn->sess;
|
||||
/*
|
||||
* FIXME: Unsolicitied NopIN support for ISER
|
||||
*/
|
||||
if (conn->conn_transport->transport_type == ISCSI_INFINIBAND)
|
||||
return;
|
||||
|
||||
if (!sess->sess_ops->SessionType)
|
||||
iscsit_start_nopin_timer(conn);
|
||||
|
@ -632,6 +737,7 @@ static int iscsi_post_login_handler(
|
|||
spin_unlock_bh(&sess->conn_lock);
|
||||
|
||||
iscsi_post_login_start_timers(conn);
|
||||
|
||||
iscsi_activate_thread_set(conn, ts);
|
||||
/*
|
||||
* Determine CPU mask to ensure connection's RX and TX kthreads
|
||||
|
@ -761,11 +867,11 @@ static void iscsi_stop_login_thread_timer(struct iscsi_np *np)
|
|||
spin_unlock_bh(&np->np_thread_lock);
|
||||
}
|
||||
|
||||
int iscsi_target_setup_login_socket(
|
||||
int iscsit_setup_np(
|
||||
struct iscsi_np *np,
|
||||
struct __kernel_sockaddr_storage *sockaddr)
|
||||
{
|
||||
struct socket *sock;
|
||||
struct socket *sock = NULL;
|
||||
int backlog = 5, ret, opt = 0, len;
|
||||
|
||||
switch (np->np_network_transport) {
|
||||
|
@ -781,15 +887,15 @@ int iscsi_target_setup_login_socket(
|
|||
np->np_ip_proto = IPPROTO_SCTP;
|
||||
np->np_sock_type = SOCK_SEQPACKET;
|
||||
break;
|
||||
case ISCSI_IWARP_TCP:
|
||||
case ISCSI_IWARP_SCTP:
|
||||
case ISCSI_INFINIBAND:
|
||||
default:
|
||||
pr_err("Unsupported network_transport: %d\n",
|
||||
np->np_network_transport);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
np->np_ip_proto = IPPROTO_TCP;
|
||||
np->np_sock_type = SOCK_STREAM;
|
||||
|
||||
ret = sock_create(sockaddr->ss_family, np->np_sock_type,
|
||||
np->np_ip_proto, &sock);
|
||||
if (ret < 0) {
|
||||
|
@ -853,7 +959,6 @@ int iscsi_target_setup_login_socket(
|
|||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
np->np_socket = NULL;
|
||||
if (sock)
|
||||
|
@ -861,21 +966,169 @@ int iscsi_target_setup_login_socket(
|
|||
return ret;
|
||||
}
|
||||
|
||||
int iscsi_target_setup_login_socket(
|
||||
struct iscsi_np *np,
|
||||
struct __kernel_sockaddr_storage *sockaddr)
|
||||
{
|
||||
struct iscsit_transport *t;
|
||||
int rc;
|
||||
|
||||
t = iscsit_get_transport(np->np_network_transport);
|
||||
if (!t)
|
||||
return -EINVAL;
|
||||
|
||||
rc = t->iscsit_setup_np(np, sockaddr);
|
||||
if (rc < 0) {
|
||||
iscsit_put_transport(t);
|
||||
return rc;
|
||||
}
|
||||
|
||||
np->np_transport = t;
|
||||
printk("Set np->np_transport to %p -> %s\n", np->np_transport,
|
||||
np->np_transport->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iscsit_accept_np(struct iscsi_np *np, struct iscsi_conn *conn)
|
||||
{
|
||||
struct socket *new_sock, *sock = np->np_socket;
|
||||
struct sockaddr_in sock_in;
|
||||
struct sockaddr_in6 sock_in6;
|
||||
int rc, err;
|
||||
|
||||
rc = kernel_accept(sock, &new_sock, 0);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
conn->sock = new_sock;
|
||||
conn->login_family = np->np_sockaddr.ss_family;
|
||||
printk("iSCSI/TCP: Setup conn->sock from new_sock: %p\n", new_sock);
|
||||
|
||||
if (np->np_sockaddr.ss_family == AF_INET6) {
|
||||
memset(&sock_in6, 0, sizeof(struct sockaddr_in6));
|
||||
|
||||
rc = conn->sock->ops->getname(conn->sock,
|
||||
(struct sockaddr *)&sock_in6, &err, 1);
|
||||
if (!rc) {
|
||||
snprintf(conn->login_ip, sizeof(conn->login_ip), "%pI6c",
|
||||
&sock_in6.sin6_addr.in6_u);
|
||||
conn->login_port = ntohs(sock_in6.sin6_port);
|
||||
}
|
||||
|
||||
rc = conn->sock->ops->getname(conn->sock,
|
||||
(struct sockaddr *)&sock_in6, &err, 0);
|
||||
if (!rc) {
|
||||
snprintf(conn->local_ip, sizeof(conn->local_ip), "%pI6c",
|
||||
&sock_in6.sin6_addr.in6_u);
|
||||
conn->local_port = ntohs(sock_in6.sin6_port);
|
||||
}
|
||||
} else {
|
||||
memset(&sock_in, 0, sizeof(struct sockaddr_in));
|
||||
|
||||
rc = conn->sock->ops->getname(conn->sock,
|
||||
(struct sockaddr *)&sock_in, &err, 1);
|
||||
if (!rc) {
|
||||
sprintf(conn->login_ip, "%pI4",
|
||||
&sock_in.sin_addr.s_addr);
|
||||
conn->login_port = ntohs(sock_in.sin_port);
|
||||
}
|
||||
|
||||
rc = conn->sock->ops->getname(conn->sock,
|
||||
(struct sockaddr *)&sock_in, &err, 0);
|
||||
if (!rc) {
|
||||
sprintf(conn->local_ip, "%pI4",
|
||||
&sock_in.sin_addr.s_addr);
|
||||
conn->local_port = ntohs(sock_in.sin_port);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iscsit_get_login_rx(struct iscsi_conn *conn, struct iscsi_login *login)
|
||||
{
|
||||
struct iscsi_login_req *login_req;
|
||||
u32 padding = 0, payload_length;
|
||||
|
||||
if (iscsi_login_rx_data(conn, login->req, ISCSI_HDR_LEN) < 0)
|
||||
return -1;
|
||||
|
||||
login_req = (struct iscsi_login_req *)login->req;
|
||||
payload_length = ntoh24(login_req->dlength);
|
||||
padding = ((-payload_length) & 3);
|
||||
|
||||
pr_debug("Got Login Command, Flags 0x%02x, ITT: 0x%08x,"
|
||||
" CmdSN: 0x%08x, ExpStatSN: 0x%08x, CID: %hu, Length: %u\n",
|
||||
login_req->flags, login_req->itt, login_req->cmdsn,
|
||||
login_req->exp_statsn, login_req->cid, payload_length);
|
||||
/*
|
||||
* Setup the initial iscsi_login values from the leading
|
||||
* login request PDU.
|
||||
*/
|
||||
if (login->first_request) {
|
||||
login_req = (struct iscsi_login_req *)login->req;
|
||||
login->leading_connection = (!login_req->tsih) ? 1 : 0;
|
||||
login->current_stage = ISCSI_LOGIN_CURRENT_STAGE(login_req->flags);
|
||||
login->version_min = login_req->min_version;
|
||||
login->version_max = login_req->max_version;
|
||||
memcpy(login->isid, login_req->isid, 6);
|
||||
login->cmd_sn = be32_to_cpu(login_req->cmdsn);
|
||||
login->init_task_tag = login_req->itt;
|
||||
login->initial_exp_statsn = be32_to_cpu(login_req->exp_statsn);
|
||||
login->cid = be16_to_cpu(login_req->cid);
|
||||
login->tsih = be16_to_cpu(login_req->tsih);
|
||||
}
|
||||
|
||||
if (iscsi_target_check_login_request(conn, login) < 0)
|
||||
return -1;
|
||||
|
||||
memset(login->req_buf, 0, MAX_KEY_VALUE_PAIRS);
|
||||
if (iscsi_login_rx_data(conn, login->req_buf,
|
||||
payload_length + padding) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iscsit_put_login_tx(struct iscsi_conn *conn, struct iscsi_login *login,
|
||||
u32 length)
|
||||
{
|
||||
if (iscsi_login_tx_data(conn, login->rsp, login->rsp_buf, length) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
iscsit_conn_set_transport(struct iscsi_conn *conn, struct iscsit_transport *t)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!t->owner) {
|
||||
conn->conn_transport = t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = try_module_get(t->owner);
|
||||
if (!rc) {
|
||||
pr_err("try_module_get() failed for %s\n", t->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
conn->conn_transport = t;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __iscsi_target_login_thread(struct iscsi_np *np)
|
||||
{
|
||||
u8 buffer[ISCSI_HDR_LEN], iscsi_opcode, zero_tsih = 0;
|
||||
int err, ret = 0, stop;
|
||||
u8 *buffer, zero_tsih = 0;
|
||||
int ret = 0, rc, stop;
|
||||
struct iscsi_conn *conn = NULL;
|
||||
struct iscsi_login *login;
|
||||
struct iscsi_portal_group *tpg = NULL;
|
||||
struct socket *new_sock, *sock;
|
||||
struct kvec iov;
|
||||
struct iscsi_login_req *pdu;
|
||||
struct sockaddr_in sock_in;
|
||||
struct sockaddr_in6 sock_in6;
|
||||
|
||||
flush_signals(current);
|
||||
sock = np->np_socket;
|
||||
|
||||
spin_lock_bh(&np->np_thread_lock);
|
||||
if (np->np_thread_state == ISCSI_NP_THREAD_RESET) {
|
||||
|
@ -886,75 +1139,76 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
|
|||
}
|
||||
spin_unlock_bh(&np->np_thread_lock);
|
||||
|
||||
if (kernel_accept(sock, &new_sock, 0) < 0) {
|
||||
spin_lock_bh(&np->np_thread_lock);
|
||||
if (np->np_thread_state == ISCSI_NP_THREAD_RESET) {
|
||||
spin_unlock_bh(&np->np_thread_lock);
|
||||
complete(&np->np_restart_comp);
|
||||
/* Get another socket */
|
||||
return 1;
|
||||
}
|
||||
spin_unlock_bh(&np->np_thread_lock);
|
||||
goto out;
|
||||
}
|
||||
iscsi_start_login_thread_timer(np);
|
||||
|
||||
conn = kzalloc(sizeof(struct iscsi_conn), GFP_KERNEL);
|
||||
if (!conn) {
|
||||
pr_err("Could not allocate memory for"
|
||||
" new connection\n");
|
||||
sock_release(new_sock);
|
||||
/* Get another socket */
|
||||
return 1;
|
||||
}
|
||||
|
||||
pr_debug("Moving to TARG_CONN_STATE_FREE.\n");
|
||||
conn->conn_state = TARG_CONN_STATE_FREE;
|
||||
conn->sock = new_sock;
|
||||
|
||||
pr_debug("Moving to TARG_CONN_STATE_XPT_UP.\n");
|
||||
conn->conn_state = TARG_CONN_STATE_XPT_UP;
|
||||
if (iscsit_conn_set_transport(conn, np->np_transport) < 0) {
|
||||
kfree(conn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate conn->conn_ops early as a failure calling
|
||||
* iscsit_tx_login_rsp() below will call tx_data().
|
||||
*/
|
||||
conn->conn_ops = kzalloc(sizeof(struct iscsi_conn_ops), GFP_KERNEL);
|
||||
if (!conn->conn_ops) {
|
||||
pr_err("Unable to allocate memory for"
|
||||
" struct iscsi_conn_ops.\n");
|
||||
goto new_sess_out;
|
||||
rc = np->np_transport->iscsit_accept_np(np, conn);
|
||||
if (rc == -ENOSYS) {
|
||||
complete(&np->np_restart_comp);
|
||||
iscsit_put_transport(conn->conn_transport);
|
||||
kfree(conn);
|
||||
conn = NULL;
|
||||
goto exit;
|
||||
} else if (rc < 0) {
|
||||
spin_lock_bh(&np->np_thread_lock);
|
||||
if (np->np_thread_state == ISCSI_NP_THREAD_RESET) {
|
||||
spin_unlock_bh(&np->np_thread_lock);
|
||||
complete(&np->np_restart_comp);
|
||||
if (ret == -ENODEV) {
|
||||
iscsit_put_transport(conn->conn_transport);
|
||||
kfree(conn);
|
||||
conn = NULL;
|
||||
goto out;
|
||||
}
|
||||
/* Get another socket */
|
||||
return 1;
|
||||
}
|
||||
spin_unlock_bh(&np->np_thread_lock);
|
||||
iscsit_put_transport(conn->conn_transport);
|
||||
kfree(conn);
|
||||
conn = NULL;
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* Perform the remaining iSCSI connection initialization items..
|
||||
*/
|
||||
if (iscsi_login_init_conn(conn) < 0)
|
||||
goto new_sess_out;
|
||||
|
||||
memset(buffer, 0, ISCSI_HDR_LEN);
|
||||
memset(&iov, 0, sizeof(struct kvec));
|
||||
iov.iov_base = buffer;
|
||||
iov.iov_len = ISCSI_HDR_LEN;
|
||||
|
||||
if (rx_data(conn, &iov, 1, ISCSI_HDR_LEN) <= 0) {
|
||||
pr_err("rx_data() returned an error.\n");
|
||||
login = iscsi_login_init_conn(conn);
|
||||
if (!login) {
|
||||
goto new_sess_out;
|
||||
}
|
||||
|
||||
iscsi_opcode = (buffer[0] & ISCSI_OPCODE_MASK);
|
||||
if (!(iscsi_opcode & ISCSI_OP_LOGIN)) {
|
||||
pr_err("First opcode is not login request,"
|
||||
" failing login request.\n");
|
||||
iscsi_start_login_thread_timer(np);
|
||||
|
||||
pr_debug("Moving to TARG_CONN_STATE_XPT_UP.\n");
|
||||
conn->conn_state = TARG_CONN_STATE_XPT_UP;
|
||||
/*
|
||||
* This will process the first login request + payload..
|
||||
*/
|
||||
rc = np->np_transport->iscsit_get_login_rx(conn, login);
|
||||
if (rc == 1)
|
||||
return 1;
|
||||
else if (rc < 0)
|
||||
goto new_sess_out;
|
||||
}
|
||||
|
||||
pdu = (struct iscsi_login_req *) buffer;
|
||||
|
||||
buffer = &login->req[0];
|
||||
pdu = (struct iscsi_login_req *)buffer;
|
||||
/*
|
||||
* Used by iscsit_tx_login_rsp() for Login Resonses PDUs
|
||||
* when Status-Class != 0.
|
||||
*/
|
||||
conn->login_itt = pdu->itt;
|
||||
conn->login_itt = pdu->itt;
|
||||
|
||||
spin_lock_bh(&np->np_thread_lock);
|
||||
if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) {
|
||||
|
@ -967,61 +1221,11 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
|
|||
}
|
||||
spin_unlock_bh(&np->np_thread_lock);
|
||||
|
||||
if (np->np_sockaddr.ss_family == AF_INET6) {
|
||||
memset(&sock_in6, 0, sizeof(struct sockaddr_in6));
|
||||
|
||||
if (conn->sock->ops->getname(conn->sock,
|
||||
(struct sockaddr *)&sock_in6, &err, 1) < 0) {
|
||||
pr_err("sock_ops->getname() failed.\n");
|
||||
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
|
||||
ISCSI_LOGIN_STATUS_TARGET_ERROR);
|
||||
goto new_sess_out;
|
||||
}
|
||||
snprintf(conn->login_ip, sizeof(conn->login_ip), "%pI6c",
|
||||
&sock_in6.sin6_addr.in6_u);
|
||||
conn->login_port = ntohs(sock_in6.sin6_port);
|
||||
|
||||
if (conn->sock->ops->getname(conn->sock,
|
||||
(struct sockaddr *)&sock_in6, &err, 0) < 0) {
|
||||
pr_err("sock_ops->getname() failed.\n");
|
||||
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
|
||||
ISCSI_LOGIN_STATUS_TARGET_ERROR);
|
||||
goto new_sess_out;
|
||||
}
|
||||
snprintf(conn->local_ip, sizeof(conn->local_ip), "%pI6c",
|
||||
&sock_in6.sin6_addr.in6_u);
|
||||
conn->local_port = ntohs(sock_in6.sin6_port);
|
||||
|
||||
} else {
|
||||
memset(&sock_in, 0, sizeof(struct sockaddr_in));
|
||||
|
||||
if (conn->sock->ops->getname(conn->sock,
|
||||
(struct sockaddr *)&sock_in, &err, 1) < 0) {
|
||||
pr_err("sock_ops->getname() failed.\n");
|
||||
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
|
||||
ISCSI_LOGIN_STATUS_TARGET_ERROR);
|
||||
goto new_sess_out;
|
||||
}
|
||||
sprintf(conn->login_ip, "%pI4", &sock_in.sin_addr.s_addr);
|
||||
conn->login_port = ntohs(sock_in.sin_port);
|
||||
|
||||
if (conn->sock->ops->getname(conn->sock,
|
||||
(struct sockaddr *)&sock_in, &err, 0) < 0) {
|
||||
pr_err("sock_ops->getname() failed.\n");
|
||||
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
|
||||
ISCSI_LOGIN_STATUS_TARGET_ERROR);
|
||||
goto new_sess_out;
|
||||
}
|
||||
sprintf(conn->local_ip, "%pI4", &sock_in.sin_addr.s_addr);
|
||||
conn->local_port = ntohs(sock_in.sin_port);
|
||||
}
|
||||
|
||||
conn->network_transport = np->np_network_transport;
|
||||
|
||||
pr_debug("Received iSCSI login request from %s on %s Network"
|
||||
" Portal %s:%hu\n", conn->login_ip,
|
||||
(conn->network_transport == ISCSI_TCP) ? "TCP" : "SCTP",
|
||||
conn->local_ip, conn->local_port);
|
||||
" Portal %s:%hu\n", conn->login_ip, np->np_transport->name,
|
||||
conn->local_ip, conn->local_port);
|
||||
|
||||
pr_debug("Moving to TARG_CONN_STATE_IN_LOGIN.\n");
|
||||
conn->conn_state = TARG_CONN_STATE_IN_LOGIN;
|
||||
|
@ -1050,13 +1254,17 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
|
|||
if (iscsi_login_non_zero_tsih_s1(conn, buffer) < 0)
|
||||
goto new_sess_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* This will process the first login request, and call
|
||||
* iscsi_target_locate_portal(), and return a valid struct iscsi_login.
|
||||
* SessionType: Discovery
|
||||
*
|
||||
* Locates Default Portal
|
||||
*
|
||||
* SessionType: Normal
|
||||
*
|
||||
* Locates Target Portal from NP -> Target IQN
|
||||
*/
|
||||
login = iscsi_target_init_negotiation(np, conn, buffer);
|
||||
if (!login) {
|
||||
rc = iscsi_target_locate_portal(np, conn, login);
|
||||
if (rc < 0) {
|
||||
tpg = conn->tpg;
|
||||
goto new_sess_out;
|
||||
}
|
||||
|
@ -1068,15 +1276,11 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
|
|||
}
|
||||
|
||||
if (zero_tsih) {
|
||||
if (iscsi_login_zero_tsih_s2(conn) < 0) {
|
||||
iscsi_target_nego_release(login, conn);
|
||||
if (iscsi_login_zero_tsih_s2(conn) < 0)
|
||||
goto new_sess_out;
|
||||
}
|
||||
} else {
|
||||
if (iscsi_login_non_zero_tsih_s2(conn, buffer) < 0) {
|
||||
iscsi_target_nego_release(login, conn);
|
||||
if (iscsi_login_non_zero_tsih_s2(conn, buffer) < 0)
|
||||
goto old_sess_out;
|
||||
}
|
||||
}
|
||||
|
||||
if (iscsi_target_start_negotiation(login, conn) < 0)
|
||||
|
@ -1153,8 +1357,18 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
|
|||
iscsi_release_param_list(conn->param_list);
|
||||
conn->param_list = NULL;
|
||||
}
|
||||
if (conn->sock)
|
||||
iscsi_target_nego_release(conn);
|
||||
|
||||
if (conn->sock) {
|
||||
sock_release(conn->sock);
|
||||
conn->sock = NULL;
|
||||
}
|
||||
|
||||
if (conn->conn_transport->iscsit_free_conn)
|
||||
conn->conn_transport->iscsit_free_conn(conn);
|
||||
|
||||
iscsit_put_transport(conn->conn_transport);
|
||||
|
||||
kfree(conn);
|
||||
|
||||
if (tpg) {
|
||||
|
@ -1172,11 +1386,13 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
|
|||
/* Wait for another socket.. */
|
||||
if (!stop)
|
||||
return 1;
|
||||
|
||||
exit:
|
||||
iscsi_stop_login_thread_timer(np);
|
||||
spin_lock_bh(&np->np_thread_lock);
|
||||
np->np_thread_state = ISCSI_NP_THREAD_EXIT;
|
||||
np->np_thread = NULL;
|
||||
spin_unlock_bh(&np->np_thread_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,14 @@
|
|||
extern int iscsi_login_setup_crypto(struct iscsi_conn *);
|
||||
extern int iscsi_check_for_session_reinstatement(struct iscsi_conn *);
|
||||
extern int iscsi_login_post_auth_non_zero_tsih(struct iscsi_conn *, u16, u32);
|
||||
extern int iscsit_setup_np(struct iscsi_np *,
|
||||
struct __kernel_sockaddr_storage *);
|
||||
extern int iscsi_target_setup_login_socket(struct iscsi_np *,
|
||||
struct __kernel_sockaddr_storage *);
|
||||
extern int iscsit_accept_np(struct iscsi_np *, struct iscsi_conn *);
|
||||
extern int iscsit_get_login_rx(struct iscsi_conn *, struct iscsi_login *);
|
||||
extern int iscsit_put_login_tx(struct iscsi_conn *, struct iscsi_login *, u32);
|
||||
extern void iscsit_free_conn(struct iscsi_np *, struct iscsi_conn *);
|
||||
extern int iscsi_target_login_thread(void *);
|
||||
extern int iscsi_login_disable_FIM_keys(struct iscsi_param_list *, struct iscsi_conn *);
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <scsi/iscsi_proto.h>
|
||||
#include <target/target_core_base.h>
|
||||
#include <target/target_core_fabric.h>
|
||||
#include <target/iscsi/iscsi_transport.h>
|
||||
|
||||
#include "iscsi_target_core.h"
|
||||
#include "iscsi_target_parameters.h"
|
||||
|
@ -169,7 +170,7 @@ static void iscsi_remove_failed_auth_entry(struct iscsi_conn *conn)
|
|||
kfree(conn->auth_protocol);
|
||||
}
|
||||
|
||||
static int iscsi_target_check_login_request(
|
||||
int iscsi_target_check_login_request(
|
||||
struct iscsi_conn *conn,
|
||||
struct iscsi_login *login)
|
||||
{
|
||||
|
@ -200,8 +201,8 @@ static int iscsi_target_check_login_request(
|
|||
return -1;
|
||||
}
|
||||
|
||||
req_csg = (login_req->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2;
|
||||
req_nsg = (login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK);
|
||||
req_csg = ISCSI_LOGIN_CURRENT_STAGE(login_req->flags);
|
||||
req_nsg = ISCSI_LOGIN_NEXT_STAGE(login_req->flags);
|
||||
|
||||
if (req_csg != login->current_stage) {
|
||||
pr_err("Initiator unexpectedly changed login stage"
|
||||
|
@ -352,11 +353,8 @@ static int iscsi_target_do_tx_login_io(struct iscsi_conn *conn, struct iscsi_log
|
|||
|
||||
padding = ((-login->rsp_length) & 3);
|
||||
|
||||
if (iscsi_login_tx_data(
|
||||
conn,
|
||||
login->rsp,
|
||||
login->rsp_buf,
|
||||
login->rsp_length + padding) < 0)
|
||||
if (conn->conn_transport->iscsit_put_login_tx(conn, login,
|
||||
login->rsp_length + padding) < 0)
|
||||
return -1;
|
||||
|
||||
login->rsp_length = 0;
|
||||
|
@ -368,72 +366,12 @@ static int iscsi_target_do_tx_login_io(struct iscsi_conn *conn, struct iscsi_log
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int iscsi_target_do_rx_login_io(struct iscsi_conn *conn, struct iscsi_login *login)
|
||||
{
|
||||
u32 padding = 0, payload_length;
|
||||
struct iscsi_login_req *login_req;
|
||||
|
||||
if (iscsi_login_rx_data(conn, login->req, ISCSI_HDR_LEN) < 0)
|
||||
return -1;
|
||||
|
||||
login_req = (struct iscsi_login_req *) login->req;
|
||||
payload_length = ntoh24(login_req->dlength);
|
||||
|
||||
pr_debug("Got Login Command, Flags 0x%02x, ITT: 0x%08x,"
|
||||
" CmdSN: 0x%08x, ExpStatSN: 0x%08x, CID: %hu, Length: %u\n",
|
||||
login_req->flags, login_req->itt, login_req->cmdsn,
|
||||
login_req->exp_statsn, login_req->cid, payload_length);
|
||||
|
||||
if (iscsi_target_check_login_request(conn, login) < 0)
|
||||
return -1;
|
||||
|
||||
padding = ((-payload_length) & 3);
|
||||
memset(login->req_buf, 0, MAX_KEY_VALUE_PAIRS);
|
||||
|
||||
if (iscsi_login_rx_data(
|
||||
conn,
|
||||
login->req_buf,
|
||||
payload_length + padding) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iscsi_target_do_login_io(struct iscsi_conn *conn, struct iscsi_login *login)
|
||||
{
|
||||
if (iscsi_target_do_tx_login_io(conn, login) < 0)
|
||||
return -1;
|
||||
|
||||
if (iscsi_target_do_rx_login_io(conn, login) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iscsi_target_get_initial_payload(
|
||||
struct iscsi_conn *conn,
|
||||
struct iscsi_login *login)
|
||||
{
|
||||
u32 padding = 0, payload_length;
|
||||
struct iscsi_login_req *login_req;
|
||||
|
||||
login_req = (struct iscsi_login_req *) login->req;
|
||||
payload_length = ntoh24(login_req->dlength);
|
||||
|
||||
pr_debug("Got Login Command, Flags 0x%02x, ITT: 0x%08x,"
|
||||
" CmdSN: 0x%08x, ExpStatSN: 0x%08x, Length: %u\n",
|
||||
login_req->flags, login_req->itt, login_req->cmdsn,
|
||||
login_req->exp_statsn, payload_length);
|
||||
|
||||
if (iscsi_target_check_login_request(conn, login) < 0)
|
||||
return -1;
|
||||
|
||||
padding = ((-payload_length) & 3);
|
||||
|
||||
if (iscsi_login_rx_data(
|
||||
conn,
|
||||
login->req_buf,
|
||||
payload_length + padding) < 0)
|
||||
if (conn->conn_transport->iscsit_get_login_rx(conn, login) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
|
@ -681,9 +619,9 @@ static int iscsi_target_do_login(struct iscsi_conn *conn, struct iscsi_login *lo
|
|||
return -1;
|
||||
}
|
||||
|
||||
switch ((login_req->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2) {
|
||||
switch (ISCSI_LOGIN_CURRENT_STAGE(login_req->flags)) {
|
||||
case 0:
|
||||
login_rsp->flags |= (0 & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK);
|
||||
login_rsp->flags &= ~ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK;
|
||||
if (iscsi_target_handle_csg_zero(conn, login) < 0)
|
||||
return -1;
|
||||
break;
|
||||
|
@ -693,6 +631,7 @@ static int iscsi_target_do_login(struct iscsi_conn *conn, struct iscsi_login *lo
|
|||
return -1;
|
||||
if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) {
|
||||
login->tsih = conn->sess->tsih;
|
||||
login->login_complete = 1;
|
||||
if (iscsi_target_do_tx_login_io(conn,
|
||||
login) < 0)
|
||||
return -1;
|
||||
|
@ -702,8 +641,7 @@ static int iscsi_target_do_login(struct iscsi_conn *conn, struct iscsi_login *lo
|
|||
default:
|
||||
pr_err("Illegal CSG: %d received from"
|
||||
" Initiator, protocol error.\n",
|
||||
(login_req->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK)
|
||||
>> 2);
|
||||
ISCSI_LOGIN_CURRENT_STAGE(login_req->flags));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -737,7 +675,7 @@ static void iscsi_initiatorname_tolower(
|
|||
/*
|
||||
* Processes the first Login Request..
|
||||
*/
|
||||
static int iscsi_target_locate_portal(
|
||||
int iscsi_target_locate_portal(
|
||||
struct iscsi_np *np,
|
||||
struct iscsi_conn *conn,
|
||||
struct iscsi_login *login)
|
||||
|
@ -753,22 +691,6 @@ static int iscsi_target_locate_portal(
|
|||
login_req = (struct iscsi_login_req *) login->req;
|
||||
payload_length = ntoh24(login_req->dlength);
|
||||
|
||||
login->first_request = 1;
|
||||
login->leading_connection = (!login_req->tsih) ? 1 : 0;
|
||||
login->current_stage =
|
||||
(login_req->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2;
|
||||
login->version_min = login_req->min_version;
|
||||
login->version_max = login_req->max_version;
|
||||
memcpy(login->isid, login_req->isid, 6);
|
||||
login->cmd_sn = be32_to_cpu(login_req->cmdsn);
|
||||
login->init_task_tag = login_req->itt;
|
||||
login->initial_exp_statsn = be32_to_cpu(login_req->exp_statsn);
|
||||
login->cid = be16_to_cpu(login_req->cid);
|
||||
login->tsih = be16_to_cpu(login_req->tsih);
|
||||
|
||||
if (iscsi_target_get_initial_payload(conn, login) < 0)
|
||||
return -1;
|
||||
|
||||
tmpbuf = kzalloc(payload_length + 1, GFP_KERNEL);
|
||||
if (!tmpbuf) {
|
||||
pr_err("Unable to allocate memory for tmpbuf.\n");
|
||||
|
@ -800,6 +722,8 @@ static int iscsi_target_locate_portal(
|
|||
start += strlen(key) + strlen(value) + 2;
|
||||
}
|
||||
|
||||
printk("i_buf: %s, s_buf: %s, t_buf: %s\n", i_buf, s_buf, t_buf);
|
||||
|
||||
/*
|
||||
* See 5.3. Login Phase.
|
||||
*/
|
||||
|
@ -958,100 +882,30 @@ static int iscsi_target_locate_portal(
|
|||
return ret;
|
||||
}
|
||||
|
||||
struct iscsi_login *iscsi_target_init_negotiation(
|
||||
struct iscsi_np *np,
|
||||
struct iscsi_conn *conn,
|
||||
char *login_pdu)
|
||||
{
|
||||
struct iscsi_login *login;
|
||||
|
||||
login = kzalloc(sizeof(struct iscsi_login), GFP_KERNEL);
|
||||
if (!login) {
|
||||
pr_err("Unable to allocate memory for struct iscsi_login.\n");
|
||||
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
|
||||
ISCSI_LOGIN_STATUS_NO_RESOURCES);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
login->req = kmemdup(login_pdu, ISCSI_HDR_LEN, GFP_KERNEL);
|
||||
if (!login->req) {
|
||||
pr_err("Unable to allocate memory for Login Request.\n");
|
||||
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
|
||||
ISCSI_LOGIN_STATUS_NO_RESOURCES);
|
||||
goto out;
|
||||
}
|
||||
|
||||
login->req_buf = kzalloc(MAX_KEY_VALUE_PAIRS, GFP_KERNEL);
|
||||
if (!login->req_buf) {
|
||||
pr_err("Unable to allocate memory for response buffer.\n");
|
||||
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
|
||||
ISCSI_LOGIN_STATUS_NO_RESOURCES);
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* SessionType: Discovery
|
||||
*
|
||||
* Locates Default Portal
|
||||
*
|
||||
* SessionType: Normal
|
||||
*
|
||||
* Locates Target Portal from NP -> Target IQN
|
||||
*/
|
||||
if (iscsi_target_locate_portal(np, conn, login) < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
return login;
|
||||
out:
|
||||
kfree(login->req);
|
||||
kfree(login->req_buf);
|
||||
kfree(login);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int iscsi_target_start_negotiation(
|
||||
struct iscsi_login *login,
|
||||
struct iscsi_conn *conn)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
login->rsp = kzalloc(ISCSI_HDR_LEN, GFP_KERNEL);
|
||||
if (!login->rsp) {
|
||||
pr_err("Unable to allocate memory for"
|
||||
" Login Response.\n");
|
||||
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
|
||||
ISCSI_LOGIN_STATUS_NO_RESOURCES);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
login->rsp_buf = kzalloc(MAX_KEY_VALUE_PAIRS, GFP_KERNEL);
|
||||
if (!login->rsp_buf) {
|
||||
pr_err("Unable to allocate memory for"
|
||||
" request buffer.\n");
|
||||
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
|
||||
ISCSI_LOGIN_STATUS_NO_RESOURCES);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
int ret;
|
||||
|
||||
ret = iscsi_target_do_login(conn, login);
|
||||
out:
|
||||
if (ret != 0)
|
||||
iscsi_remove_failed_auth_entry(conn);
|
||||
|
||||
iscsi_target_nego_release(login, conn);
|
||||
iscsi_target_nego_release(conn);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void iscsi_target_nego_release(
|
||||
struct iscsi_login *login,
|
||||
struct iscsi_conn *conn)
|
||||
void iscsi_target_nego_release(struct iscsi_conn *conn)
|
||||
{
|
||||
kfree(login->req);
|
||||
kfree(login->rsp);
|
||||
struct iscsi_login *login = conn->conn_login;
|
||||
|
||||
if (!login)
|
||||
return;
|
||||
|
||||
kfree(login->req_buf);
|
||||
kfree(login->rsp_buf);
|
||||
kfree(login);
|
||||
|
||||
conn->conn_login = NULL;
|
||||
}
|
||||
|
|
|
@ -7,11 +7,14 @@
|
|||
extern void convert_null_to_semi(char *, int);
|
||||
extern int extract_param(const char *, const char *, unsigned int, char *,
|
||||
unsigned char *);
|
||||
extern struct iscsi_login *iscsi_target_init_negotiation(
|
||||
struct iscsi_np *, struct iscsi_conn *, char *);
|
||||
extern int iscsi_target_check_login_request(struct iscsi_conn *,
|
||||
struct iscsi_login *);
|
||||
extern int iscsi_target_get_initial_payload(struct iscsi_conn *,
|
||||
struct iscsi_login *);
|
||||
extern int iscsi_target_locate_portal(struct iscsi_np *, struct iscsi_conn *,
|
||||
struct iscsi_login *);
|
||||
extern int iscsi_target_start_negotiation(
|
||||
struct iscsi_login *, struct iscsi_conn *);
|
||||
extern void iscsi_target_nego_release(
|
||||
struct iscsi_login *, struct iscsi_conn *);
|
||||
extern void iscsi_target_nego_release(struct iscsi_conn *);
|
||||
|
||||
#endif /* ISCSI_TARGET_NEGO_H */
|
||||
|
|
|
@ -59,7 +59,7 @@ int iscsi_login_tx_data(
|
|||
char *text_buf,
|
||||
int text_length)
|
||||
{
|
||||
int length, tx_sent;
|
||||
int length, tx_sent, iov_cnt = 1;
|
||||
struct kvec iov[2];
|
||||
|
||||
length = (ISCSI_HDR_LEN + text_length);
|
||||
|
@ -67,8 +67,12 @@ int iscsi_login_tx_data(
|
|||
memset(&iov[0], 0, 2 * sizeof(struct kvec));
|
||||
iov[0].iov_len = ISCSI_HDR_LEN;
|
||||
iov[0].iov_base = pdu_buf;
|
||||
iov[1].iov_len = text_length;
|
||||
iov[1].iov_base = text_buf;
|
||||
|
||||
if (text_buf && text_length) {
|
||||
iov[1].iov_len = text_length;
|
||||
iov[1].iov_base = text_buf;
|
||||
iov_cnt++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initial Marker-less Interval.
|
||||
|
@ -77,7 +81,7 @@ int iscsi_login_tx_data(
|
|||
*/
|
||||
conn->if_marker += length;
|
||||
|
||||
tx_sent = tx_data(conn, &iov[0], 2, length);
|
||||
tx_sent = tx_data(conn, &iov[0], iov_cnt, length);
|
||||
if (tx_sent != length) {
|
||||
pr_err("tx_data returned %d, expecting %d.\n",
|
||||
tx_sent, length);
|
||||
|
@ -429,6 +433,28 @@ int iscsi_create_default_params(struct iscsi_param_list **param_list_ptr)
|
|||
TYPERANGE_MARKINT, USE_INITIAL_ONLY);
|
||||
if (!param)
|
||||
goto out;
|
||||
/*
|
||||
* Extra parameters for ISER from RFC-5046
|
||||
*/
|
||||
param = iscsi_set_default_param(pl, RDMAEXTENTIONS, INITIAL_RDMAEXTENTIONS,
|
||||
PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH,
|
||||
TYPERANGE_BOOL_AND, USE_LEADING_ONLY);
|
||||
if (!param)
|
||||
goto out;
|
||||
|
||||
param = iscsi_set_default_param(pl, INITIATORRECVDATASEGMENTLENGTH,
|
||||
INITIAL_INITIATORRECVDATASEGMENTLENGTH,
|
||||
PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH,
|
||||
TYPERANGE_512_TO_16777215, USE_ALL);
|
||||
if (!param)
|
||||
goto out;
|
||||
|
||||
param = iscsi_set_default_param(pl, TARGETRECVDATASEGMENTLENGTH,
|
||||
INITIAL_TARGETRECVDATASEGMENTLENGTH,
|
||||
PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH,
|
||||
TYPERANGE_512_TO_16777215, USE_ALL);
|
||||
if (!param)
|
||||
goto out;
|
||||
|
||||
*param_list_ptr = pl;
|
||||
return 0;
|
||||
|
@ -438,19 +464,23 @@ int iscsi_create_default_params(struct iscsi_param_list **param_list_ptr)
|
|||
}
|
||||
|
||||
int iscsi_set_keys_to_negotiate(
|
||||
int sessiontype,
|
||||
struct iscsi_param_list *param_list)
|
||||
struct iscsi_param_list *param_list,
|
||||
bool iser)
|
||||
{
|
||||
struct iscsi_param *param;
|
||||
|
||||
param_list->iser = iser;
|
||||
|
||||
list_for_each_entry(param, ¶m_list->param_list, p_list) {
|
||||
param->state = 0;
|
||||
if (!strcmp(param->name, AUTHMETHOD)) {
|
||||
SET_PSTATE_NEGOTIATE(param);
|
||||
} else if (!strcmp(param->name, HEADERDIGEST)) {
|
||||
SET_PSTATE_NEGOTIATE(param);
|
||||
if (iser == false)
|
||||
SET_PSTATE_NEGOTIATE(param);
|
||||
} else if (!strcmp(param->name, DATADIGEST)) {
|
||||
SET_PSTATE_NEGOTIATE(param);
|
||||
if (iser == false)
|
||||
SET_PSTATE_NEGOTIATE(param);
|
||||
} else if (!strcmp(param->name, MAXCONNECTIONS)) {
|
||||
SET_PSTATE_NEGOTIATE(param);
|
||||
} else if (!strcmp(param->name, TARGETNAME)) {
|
||||
|
@ -469,7 +499,8 @@ int iscsi_set_keys_to_negotiate(
|
|||
} else if (!strcmp(param->name, IMMEDIATEDATA)) {
|
||||
SET_PSTATE_NEGOTIATE(param);
|
||||
} else if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) {
|
||||
SET_PSTATE_NEGOTIATE(param);
|
||||
if (iser == false)
|
||||
SET_PSTATE_NEGOTIATE(param);
|
||||
} else if (!strcmp(param->name, MAXXMITDATASEGMENTLENGTH)) {
|
||||
continue;
|
||||
} else if (!strcmp(param->name, MAXBURSTLENGTH)) {
|
||||
|
@ -498,6 +529,15 @@ int iscsi_set_keys_to_negotiate(
|
|||
SET_PSTATE_NEGOTIATE(param);
|
||||
} else if (!strcmp(param->name, OFMARKINT)) {
|
||||
SET_PSTATE_NEGOTIATE(param);
|
||||
} else if (!strcmp(param->name, RDMAEXTENTIONS)) {
|
||||
if (iser == true)
|
||||
SET_PSTATE_NEGOTIATE(param);
|
||||
} else if (!strcmp(param->name, INITIATORRECVDATASEGMENTLENGTH)) {
|
||||
if (iser == true)
|
||||
SET_PSTATE_NEGOTIATE(param);
|
||||
} else if (!strcmp(param->name, TARGETRECVDATASEGMENTLENGTH)) {
|
||||
if (iser == true)
|
||||
SET_PSTATE_NEGOTIATE(param);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -540,6 +580,12 @@ int iscsi_set_keys_irrelevant_for_discovery(
|
|||
param->state &= ~PSTATE_NEGOTIATE;
|
||||
else if (!strcmp(param->name, OFMARKINT))
|
||||
param->state &= ~PSTATE_NEGOTIATE;
|
||||
else if (!strcmp(param->name, RDMAEXTENTIONS))
|
||||
param->state &= ~PSTATE_NEGOTIATE;
|
||||
else if (!strcmp(param->name, INITIATORRECVDATASEGMENTLENGTH))
|
||||
param->state &= ~PSTATE_NEGOTIATE;
|
||||
else if (!strcmp(param->name, TARGETRECVDATASEGMENTLENGTH))
|
||||
param->state &= ~PSTATE_NEGOTIATE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1755,6 +1801,9 @@ void iscsi_set_connection_parameters(
|
|||
* this key is not sent over the wire.
|
||||
*/
|
||||
if (!strcmp(param->name, MAXXMITDATASEGMENTLENGTH)) {
|
||||
if (param_list->iser == true)
|
||||
continue;
|
||||
|
||||
ops->MaxXmitDataSegmentLength =
|
||||
simple_strtoul(param->value, &tmpptr, 0);
|
||||
pr_debug("MaxXmitDataSegmentLength: %s\n",
|
||||
|
@ -1800,6 +1849,22 @@ void iscsi_set_connection_parameters(
|
|||
simple_strtoul(param->value, &tmpptr, 0);
|
||||
pr_debug("IFMarkInt: %s\n",
|
||||
param->value);
|
||||
} else if (!strcmp(param->name, INITIATORRECVDATASEGMENTLENGTH)) {
|
||||
ops->InitiatorRecvDataSegmentLength =
|
||||
simple_strtoul(param->value, &tmpptr, 0);
|
||||
pr_debug("InitiatorRecvDataSegmentLength: %s\n",
|
||||
param->value);
|
||||
ops->MaxRecvDataSegmentLength =
|
||||
ops->InitiatorRecvDataSegmentLength;
|
||||
pr_debug("Set MRDSL from InitiatorRecvDataSegmentLength\n");
|
||||
} else if (!strcmp(param->name, TARGETRECVDATASEGMENTLENGTH)) {
|
||||
ops->TargetRecvDataSegmentLength =
|
||||
simple_strtoul(param->value, &tmpptr, 0);
|
||||
pr_debug("TargetRecvDataSegmentLength: %s\n",
|
||||
param->value);
|
||||
ops->MaxXmitDataSegmentLength =
|
||||
ops->TargetRecvDataSegmentLength;
|
||||
pr_debug("Set MXDSL from TargetRecvDataSegmentLength\n");
|
||||
}
|
||||
}
|
||||
pr_debug("----------------------------------------------------"
|
||||
|
@ -1912,6 +1977,10 @@ void iscsi_set_session_parameters(
|
|||
ops->SessionType = !strcmp(param->value, DISCOVERY);
|
||||
pr_debug("SessionType: %s\n",
|
||||
param->value);
|
||||
} else if (!strcmp(param->name, RDMAEXTENTIONS)) {
|
||||
ops->RDMAExtensions = !strcmp(param->value, YES);
|
||||
pr_debug("RDMAExtensions: %s\n",
|
||||
param->value);
|
||||
}
|
||||
}
|
||||
pr_debug("----------------------------------------------------"
|
||||
|
|
|
@ -27,7 +27,7 @@ extern void iscsi_dump_conn_ops(struct iscsi_conn_ops *);
|
|||
extern void iscsi_dump_sess_ops(struct iscsi_sess_ops *);
|
||||
extern void iscsi_print_params(struct iscsi_param_list *);
|
||||
extern int iscsi_create_default_params(struct iscsi_param_list **);
|
||||
extern int iscsi_set_keys_to_negotiate(int, struct iscsi_param_list *);
|
||||
extern int iscsi_set_keys_to_negotiate(struct iscsi_param_list *, bool);
|
||||
extern int iscsi_set_keys_irrelevant_for_discovery(struct iscsi_param_list *);
|
||||
extern int iscsi_copy_param_list(struct iscsi_param_list **,
|
||||
struct iscsi_param_list *, int);
|
||||
|
@ -88,6 +88,13 @@ extern void iscsi_set_session_parameters(struct iscsi_sess_ops *,
|
|||
#define X_EXTENSIONKEY_CISCO_NEW "X-com.cisco.protocol"
|
||||
#define X_EXTENSIONKEY_CISCO_OLD "X-com.cisco.iscsi.draft"
|
||||
|
||||
/*
|
||||
* Parameter names of iSCSI Extentions for RDMA (iSER). See RFC-5046
|
||||
*/
|
||||
#define RDMAEXTENTIONS "RDMAExtensions"
|
||||
#define INITIATORRECVDATASEGMENTLENGTH "InitiatorRecvDataSegmentLength"
|
||||
#define TARGETRECVDATASEGMENTLENGTH "TargetRecvDataSegmentLength"
|
||||
|
||||
/*
|
||||
* For AuthMethod.
|
||||
*/
|
||||
|
@ -132,6 +139,13 @@ extern void iscsi_set_session_parameters(struct iscsi_sess_ops *,
|
|||
#define INITIAL_IFMARKINT "2048~65535"
|
||||
#define INITIAL_OFMARKINT "2048~65535"
|
||||
|
||||
/*
|
||||
* Initial values for iSER parameters following RFC-5046 Section 6
|
||||
*/
|
||||
#define INITIAL_RDMAEXTENTIONS NO
|
||||
#define INITIAL_INITIATORRECVDATASEGMENTLENGTH "262144"
|
||||
#define INITIAL_TARGETRECVDATASEGMENTLENGTH "8192"
|
||||
|
||||
/*
|
||||
* For [Header,Data]Digests.
|
||||
*/
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <scsi/iscsi_proto.h>
|
||||
#include <target/target_core_base.h>
|
||||
#include <target/target_core_fabric.h>
|
||||
#include <target/iscsi/iscsi_transport.h>
|
||||
|
||||
#include "iscsi_target_core.h"
|
||||
#include "iscsi_target_seq_pdu_list.h"
|
||||
|
@ -301,7 +302,7 @@ static int iscsit_task_reassign_complete_write(
|
|||
/*
|
||||
* iscsit_build_r2ts_for_cmd() can handle the rest from here.
|
||||
*/
|
||||
return iscsit_build_r2ts_for_cmd(cmd, conn, true);
|
||||
return conn->conn_transport->iscsit_get_dataout(conn, cmd, true);
|
||||
}
|
||||
|
||||
static int iscsit_task_reassign_complete_read(
|
||||
|
@ -471,6 +472,7 @@ int iscsit_tmr_post_handler(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(iscsit_tmr_post_handler);
|
||||
|
||||
/*
|
||||
* Nothing to do here, but leave it for good measure. :-)
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include "iscsi_target.h"
|
||||
#include "iscsi_target_parameters.h"
|
||||
|
||||
#include <target/iscsi/iscsi_transport.h>
|
||||
|
||||
struct iscsi_portal_group *iscsit_alloc_portal_group(struct iscsi_tiqn *tiqn, u16 tpgt)
|
||||
{
|
||||
struct iscsi_portal_group *tpg;
|
||||
|
@ -508,7 +510,7 @@ struct iscsi_tpg_np *iscsit_tpg_add_network_portal(
|
|||
|
||||
pr_debug("CORE[%s] - Added Network Portal: %s:%hu,%hu on %s\n",
|
||||
tpg->tpg_tiqn->tiqn, np->np_ip, np->np_port, tpg->tpgt,
|
||||
(np->np_network_transport == ISCSI_TCP) ? "TCP" : "SCTP");
|
||||
np->np_transport->name);
|
||||
|
||||
return tpg_np;
|
||||
}
|
||||
|
@ -522,7 +524,7 @@ static int iscsit_tpg_release_np(
|
|||
|
||||
pr_debug("CORE[%s] - Removed Network Portal: %s:%hu,%hu on %s\n",
|
||||
tpg->tpg_tiqn->tiqn, np->np_ip, np->np_port, tpg->tpgt,
|
||||
(np->np_network_transport == ISCSI_TCP) ? "TCP" : "SCTP");
|
||||
np->np_transport->name);
|
||||
|
||||
tpg_np->tpg_np = NULL;
|
||||
tpg_np->tpg = NULL;
|
||||
|
|
55
drivers/target/iscsi/iscsi_target_transport.c
Normal file
55
drivers/target/iscsi/iscsi_target_transport.c
Normal file
|
@ -0,0 +1,55 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/list.h>
|
||||
#include <target/iscsi/iscsi_transport.h>
|
||||
|
||||
static LIST_HEAD(g_transport_list);
|
||||
static DEFINE_MUTEX(transport_mutex);
|
||||
|
||||
struct iscsit_transport *iscsit_get_transport(int type)
|
||||
{
|
||||
struct iscsit_transport *t;
|
||||
|
||||
mutex_lock(&transport_mutex);
|
||||
list_for_each_entry(t, &g_transport_list, t_node) {
|
||||
if (t->transport_type == type) {
|
||||
if (t->owner && !try_module_get(t->owner)) {
|
||||
t = NULL;
|
||||
}
|
||||
mutex_unlock(&transport_mutex);
|
||||
return t;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&transport_mutex);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void iscsit_put_transport(struct iscsit_transport *t)
|
||||
{
|
||||
if (t->owner)
|
||||
module_put(t->owner);
|
||||
}
|
||||
|
||||
int iscsit_register_transport(struct iscsit_transport *t)
|
||||
{
|
||||
INIT_LIST_HEAD(&t->t_node);
|
||||
|
||||
mutex_lock(&transport_mutex);
|
||||
list_add_tail(&t->t_node, &g_transport_list);
|
||||
mutex_unlock(&transport_mutex);
|
||||
|
||||
pr_debug("Registered iSCSI transport: %s\n", t->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(iscsit_register_transport);
|
||||
|
||||
void iscsit_unregister_transport(struct iscsit_transport *t)
|
||||
{
|
||||
mutex_lock(&transport_mutex);
|
||||
list_del(&t->t_node);
|
||||
mutex_unlock(&transport_mutex);
|
||||
|
||||
pr_debug("Unregistered iSCSI transport: %s\n", t->name);
|
||||
}
|
||||
EXPORT_SYMBOL(iscsit_unregister_transport);
|
|
@ -24,6 +24,7 @@
|
|||
#include <target/target_core_base.h>
|
||||
#include <target/target_core_fabric.h>
|
||||
#include <target/target_core_configfs.h>
|
||||
#include <target/iscsi/iscsi_transport.h>
|
||||
|
||||
#include "iscsi_target_core.h"
|
||||
#include "iscsi_target_parameters.h"
|
||||
|
@ -148,6 +149,18 @@ void iscsit_free_r2ts_from_list(struct iscsi_cmd *cmd)
|
|||
spin_unlock_bh(&cmd->r2t_lock);
|
||||
}
|
||||
|
||||
struct iscsi_cmd *iscsit_alloc_cmd(struct iscsi_conn *conn, gfp_t gfp_mask)
|
||||
{
|
||||
struct iscsi_cmd *cmd;
|
||||
|
||||
cmd = kmem_cache_zalloc(lio_cmd_cache, gfp_mask);
|
||||
if (!cmd)
|
||||
return NULL;
|
||||
|
||||
cmd->release_cmd = &iscsit_release_cmd;
|
||||
return cmd;
|
||||
}
|
||||
|
||||
/*
|
||||
* May be called from software interrupt (timer) context for allocating
|
||||
* iSCSI NopINs.
|
||||
|
@ -156,13 +169,12 @@ struct iscsi_cmd *iscsit_allocate_cmd(struct iscsi_conn *conn, gfp_t gfp_mask)
|
|||
{
|
||||
struct iscsi_cmd *cmd;
|
||||
|
||||
cmd = kmem_cache_zalloc(lio_cmd_cache, gfp_mask);
|
||||
cmd = conn->conn_transport->iscsit_alloc_cmd(conn, gfp_mask);
|
||||
if (!cmd) {
|
||||
pr_err("Unable to allocate memory for struct iscsi_cmd.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cmd->conn = conn;
|
||||
cmd->conn = conn;
|
||||
INIT_LIST_HEAD(&cmd->i_conn_node);
|
||||
INIT_LIST_HEAD(&cmd->datain_list);
|
||||
INIT_LIST_HEAD(&cmd->cmd_r2t_list);
|
||||
|
@ -175,6 +187,7 @@ struct iscsi_cmd *iscsit_allocate_cmd(struct iscsi_conn *conn, gfp_t gfp_mask)
|
|||
|
||||
return cmd;
|
||||
}
|
||||
EXPORT_SYMBOL(iscsit_allocate_cmd);
|
||||
|
||||
struct iscsi_seq *iscsit_get_seq_holder_for_datain(
|
||||
struct iscsi_cmd *cmd,
|
||||
|
@ -304,6 +317,7 @@ int iscsit_sequence_cmd(
|
|||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(iscsit_sequence_cmd);
|
||||
|
||||
int iscsit_check_unsolicited_dataout(struct iscsi_cmd *cmd, unsigned char *buf)
|
||||
{
|
||||
|
@ -689,6 +703,11 @@ void iscsit_free_cmd(struct iscsi_cmd *cmd)
|
|||
*/
|
||||
switch (cmd->iscsi_opcode) {
|
||||
case ISCSI_OP_SCSI_CMD:
|
||||
if (cmd->data_direction == DMA_TO_DEVICE)
|
||||
iscsit_stop_dataout_timer(cmd);
|
||||
/*
|
||||
* Fallthrough
|
||||
*/
|
||||
case ISCSI_OP_SCSI_TMFUNC:
|
||||
transport_generic_free_cmd(&cmd->se_cmd, 1);
|
||||
break;
|
||||
|
@ -704,7 +723,7 @@ void iscsit_free_cmd(struct iscsi_cmd *cmd)
|
|||
}
|
||||
/* Fall-through */
|
||||
default:
|
||||
iscsit_release_cmd(cmd);
|
||||
cmd->release_cmd(cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1226,34 +1245,19 @@ int iscsit_fe_sendpage_sg(
|
|||
*/
|
||||
int iscsit_tx_login_rsp(struct iscsi_conn *conn, u8 status_class, u8 status_detail)
|
||||
{
|
||||
u8 iscsi_hdr[ISCSI_HDR_LEN];
|
||||
int err;
|
||||
struct kvec iov;
|
||||
struct iscsi_login_rsp *hdr;
|
||||
struct iscsi_login *login = conn->conn_login;
|
||||
|
||||
login->login_failed = 1;
|
||||
iscsit_collect_login_stats(conn, status_class, status_detail);
|
||||
|
||||
memset(&iov, 0, sizeof(struct kvec));
|
||||
memset(&iscsi_hdr, 0x0, ISCSI_HDR_LEN);
|
||||
|
||||
hdr = (struct iscsi_login_rsp *)&iscsi_hdr;
|
||||
hdr = (struct iscsi_login_rsp *)&login->rsp[0];
|
||||
hdr->opcode = ISCSI_OP_LOGIN_RSP;
|
||||
hdr->status_class = status_class;
|
||||
hdr->status_detail = status_detail;
|
||||
hdr->itt = conn->login_itt;
|
||||
|
||||
iov.iov_base = &iscsi_hdr;
|
||||
iov.iov_len = ISCSI_HDR_LEN;
|
||||
|
||||
PRINT_BUFF(iscsi_hdr, ISCSI_HDR_LEN);
|
||||
|
||||
err = tx_data(conn, &iov, 1, ISCSI_HDR_LEN);
|
||||
if (err != ISCSI_HDR_LEN) {
|
||||
pr_err("tx_data returned less than expected\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return conn->conn_transport->iscsit_put_login_tx(conn, login, 0);
|
||||
}
|
||||
|
||||
void iscsit_print_session_params(struct iscsi_session *sess)
|
||||
|
@ -1432,7 +1436,8 @@ void iscsit_collect_login_stats(
|
|||
strcpy(ls->last_intr_fail_name,
|
||||
(intrname ? intrname->value : "Unknown"));
|
||||
|
||||
ls->last_intr_fail_ip_family = conn->sock->sk->sk_family;
|
||||
ls->last_intr_fail_ip_family = conn->login_family;
|
||||
|
||||
snprintf(ls->last_intr_fail_ip_addr, IPV6_ADDRESS_SPACE,
|
||||
"%s", conn->login_ip);
|
||||
ls->last_fail_time = get_jiffies_64();
|
||||
|
|
|
@ -8,6 +8,7 @@ extern struct iscsi_r2t *iscsit_get_r2t_for_eos(struct iscsi_cmd *, u32, u32);
|
|||
extern struct iscsi_r2t *iscsit_get_r2t_from_list(struct iscsi_cmd *);
|
||||
extern void iscsit_free_r2t(struct iscsi_r2t *, struct iscsi_cmd *);
|
||||
extern void iscsit_free_r2ts_from_list(struct iscsi_cmd *);
|
||||
extern struct iscsi_cmd *iscsit_alloc_cmd(struct iscsi_conn *, gfp_t);
|
||||
extern struct iscsi_cmd *iscsit_allocate_cmd(struct iscsi_conn *, gfp_t);
|
||||
extern struct iscsi_seq *iscsit_get_seq_holder_for_datain(struct iscsi_cmd *, u32);
|
||||
extern struct iscsi_seq *iscsit_get_seq_holder_for_r2t(struct iscsi_cmd *);
|
||||
|
|
|
@ -30,8 +30,10 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/falloc.h>
|
||||
#include <scsi/scsi.h>
|
||||
#include <scsi/scsi_host.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include <target/target_core_base.h>
|
||||
#include <target/target_core_backend.h>
|
||||
|
@ -166,6 +168,33 @@ static int fd_configure_device(struct se_device *dev)
|
|||
" block_device blocks: %llu logical_block_size: %d\n",
|
||||
dev_size, div_u64(dev_size, fd_dev->fd_block_size),
|
||||
fd_dev->fd_block_size);
|
||||
/*
|
||||
* Check if the underlying struct block_device request_queue supports
|
||||
* the QUEUE_FLAG_DISCARD bit for UNMAP/WRITE_SAME in SCSI + TRIM
|
||||
* in ATA and we need to set TPE=1
|
||||
*/
|
||||
if (blk_queue_discard(q)) {
|
||||
dev->dev_attrib.max_unmap_lba_count =
|
||||
q->limits.max_discard_sectors;
|
||||
/*
|
||||
* Currently hardcoded to 1 in Linux/SCSI code..
|
||||
*/
|
||||
dev->dev_attrib.max_unmap_block_desc_count = 1;
|
||||
dev->dev_attrib.unmap_granularity =
|
||||
q->limits.discard_granularity >> 9;
|
||||
dev->dev_attrib.unmap_granularity_alignment =
|
||||
q->limits.discard_alignment;
|
||||
pr_debug("IFILE: BLOCK Discard support available,"
|
||||
" disabled by default\n");
|
||||
}
|
||||
/*
|
||||
* Enable write same emulation for IBLOCK and use 0xFFFF as
|
||||
* the smaller WRITE_SAME(10) only has a two-byte block count.
|
||||
*/
|
||||
dev->dev_attrib.max_write_same_len = 0xFFFF;
|
||||
|
||||
if (blk_queue_nonrot(q))
|
||||
dev->dev_attrib.is_nonrot = 1;
|
||||
} else {
|
||||
if (!(fd_dev->fbd_flags & FBDF_HAS_SIZE)) {
|
||||
pr_err("FILEIO: Missing fd_dev_size="
|
||||
|
@ -176,6 +205,23 @@ static int fd_configure_device(struct se_device *dev)
|
|||
|
||||
dev->dev_attrib.hw_block_size = FD_BLOCKSIZE;
|
||||
dev->dev_attrib.hw_max_sectors = FD_MAX_SECTORS;
|
||||
|
||||
/*
|
||||
* Limit UNMAP emulation to 8k Number of LBAs (NoLB)
|
||||
*/
|
||||
dev->dev_attrib.max_unmap_lba_count = 0x2000;
|
||||
/*
|
||||
* Currently hardcoded to 1 in Linux/SCSI code..
|
||||
*/
|
||||
dev->dev_attrib.max_unmap_block_desc_count = 1;
|
||||
dev->dev_attrib.unmap_granularity = 1;
|
||||
dev->dev_attrib.unmap_granularity_alignment = 0;
|
||||
|
||||
/*
|
||||
* Limit WRITE_SAME w/ UNMAP=0 emulation to 8k Number of LBAs (NoLB)
|
||||
* based upon struct iovec limit for vfs_writev()
|
||||
*/
|
||||
dev->dev_attrib.max_write_same_len = 0x1000;
|
||||
}
|
||||
|
||||
fd_dev->fd_block_size = dev->dev_attrib.hw_block_size;
|
||||
|
@ -190,11 +236,6 @@ static int fd_configure_device(struct se_device *dev)
|
|||
|
||||
fd_dev->fd_dev_id = fd_host->fd_host_dev_id_count++;
|
||||
fd_dev->fd_queue_depth = dev->queue_depth;
|
||||
/*
|
||||
* Limit WRITE_SAME w/ UNMAP=0 emulation to 8k Number of LBAs (NoLB)
|
||||
* based upon struct iovec limit for vfs_writev()
|
||||
*/
|
||||
dev->dev_attrib.max_write_same_len = 0x1000;
|
||||
|
||||
pr_debug("CORE_FILE[%u] - Added TCM FILEIO Device ID: %u at %s,"
|
||||
" %llu total bytes\n", fd_host->fd_host_id, fd_dev->fd_dev_id,
|
||||
|
@ -441,6 +482,75 @@ fd_execute_write_same(struct se_cmd *cmd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static sense_reason_t
|
||||
fd_do_unmap(struct se_cmd *cmd, void *priv, sector_t lba, sector_t nolb)
|
||||
{
|
||||
struct file *file = priv;
|
||||
struct inode *inode = file->f_mapping->host;
|
||||
int ret;
|
||||
|
||||
if (S_ISBLK(inode->i_mode)) {
|
||||
/* The backend is block device, use discard */
|
||||
struct block_device *bdev = inode->i_bdev;
|
||||
|
||||
ret = blkdev_issue_discard(bdev, lba,
|
||||
nolb, GFP_KERNEL, 0);
|
||||
if (ret < 0) {
|
||||
pr_warn("FILEIO: blkdev_issue_discard() failed: %d\n",
|
||||
ret);
|
||||
return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
|
||||
}
|
||||
} else {
|
||||
/* The backend is normal file, use fallocate */
|
||||
struct se_device *se_dev = cmd->se_dev;
|
||||
loff_t pos = lba * se_dev->dev_attrib.block_size;
|
||||
unsigned int len = nolb * se_dev->dev_attrib.block_size;
|
||||
int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE;
|
||||
|
||||
if (!file->f_op->fallocate)
|
||||
return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
|
||||
|
||||
ret = file->f_op->fallocate(file, mode, pos, len);
|
||||
if (ret < 0) {
|
||||
pr_warn("FILEIO: fallocate() failed: %d\n", ret);
|
||||
return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static sense_reason_t
|
||||
fd_execute_write_same_unmap(struct se_cmd *cmd)
|
||||
{
|
||||
struct se_device *se_dev = cmd->se_dev;
|
||||
struct fd_dev *fd_dev = FD_DEV(se_dev);
|
||||
struct file *file = fd_dev->fd_file;
|
||||
sector_t lba = cmd->t_task_lba;
|
||||
sector_t nolb = sbc_get_write_same_sectors(cmd);
|
||||
int ret;
|
||||
|
||||
if (!nolb) {
|
||||
target_complete_cmd(cmd, SAM_STAT_GOOD);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = fd_do_unmap(cmd, file, lba, nolb);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
target_complete_cmd(cmd, GOOD);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static sense_reason_t
|
||||
fd_execute_unmap(struct se_cmd *cmd)
|
||||
{
|
||||
struct file *file = FD_DEV(cmd->se_dev)->fd_file;
|
||||
|
||||
return sbc_execute_unmap(cmd, fd_do_unmap, file);
|
||||
}
|
||||
|
||||
static sense_reason_t
|
||||
fd_execute_rw(struct se_cmd *cmd)
|
||||
{
|
||||
|
@ -600,6 +710,8 @@ static struct sbc_ops fd_sbc_ops = {
|
|||
.execute_rw = fd_execute_rw,
|
||||
.execute_sync_cache = fd_execute_sync_cache,
|
||||
.execute_write_same = fd_execute_write_same,
|
||||
.execute_write_same_unmap = fd_execute_write_same_unmap,
|
||||
.execute_unmap = fd_execute_unmap,
|
||||
};
|
||||
|
||||
static sense_reason_t
|
||||
|
|
|
@ -379,105 +379,41 @@ iblock_execute_sync_cache(struct se_cmd *cmd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static sense_reason_t
|
||||
iblock_do_unmap(struct se_cmd *cmd, void *priv,
|
||||
sector_t lba, sector_t nolb)
|
||||
{
|
||||
struct block_device *bdev = priv;
|
||||
int ret;
|
||||
|
||||
ret = blkdev_issue_discard(bdev, lba, nolb, GFP_KERNEL, 0);
|
||||
if (ret < 0) {
|
||||
pr_err("blkdev_issue_discard() failed: %d\n", ret);
|
||||
return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static sense_reason_t
|
||||
iblock_execute_unmap(struct se_cmd *cmd)
|
||||
{
|
||||
struct se_device *dev = cmd->se_dev;
|
||||
struct iblock_dev *ib_dev = IBLOCK_DEV(dev);
|
||||
unsigned char *buf, *ptr = NULL;
|
||||
sector_t lba;
|
||||
int size;
|
||||
u32 range;
|
||||
sense_reason_t ret = 0;
|
||||
int dl, bd_dl, err;
|
||||
struct block_device *bdev = IBLOCK_DEV(cmd->se_dev)->ibd_bd;
|
||||
|
||||
/* We never set ANC_SUP */
|
||||
if (cmd->t_task_cdb[1])
|
||||
return TCM_INVALID_CDB_FIELD;
|
||||
|
||||
if (cmd->data_length == 0) {
|
||||
target_complete_cmd(cmd, SAM_STAT_GOOD);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cmd->data_length < 8) {
|
||||
pr_warn("UNMAP parameter list length %u too small\n",
|
||||
cmd->data_length);
|
||||
return TCM_PARAMETER_LIST_LENGTH_ERROR;
|
||||
}
|
||||
|
||||
buf = transport_kmap_data_sg(cmd);
|
||||
if (!buf)
|
||||
return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
|
||||
|
||||
dl = get_unaligned_be16(&buf[0]);
|
||||
bd_dl = get_unaligned_be16(&buf[2]);
|
||||
|
||||
size = cmd->data_length - 8;
|
||||
if (bd_dl > size)
|
||||
pr_warn("UNMAP parameter list length %u too small, ignoring bd_dl %u\n",
|
||||
cmd->data_length, bd_dl);
|
||||
else
|
||||
size = bd_dl;
|
||||
|
||||
if (size / 16 > dev->dev_attrib.max_unmap_block_desc_count) {
|
||||
ret = TCM_INVALID_PARAMETER_LIST;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* First UNMAP block descriptor starts at 8 byte offset */
|
||||
ptr = &buf[8];
|
||||
pr_debug("UNMAP: Sub: %s Using dl: %u bd_dl: %u size: %u"
|
||||
" ptr: %p\n", dev->transport->name, dl, bd_dl, size, ptr);
|
||||
|
||||
while (size >= 16) {
|
||||
lba = get_unaligned_be64(&ptr[0]);
|
||||
range = get_unaligned_be32(&ptr[8]);
|
||||
pr_debug("UNMAP: Using lba: %llu and range: %u\n",
|
||||
(unsigned long long)lba, range);
|
||||
|
||||
if (range > dev->dev_attrib.max_unmap_lba_count) {
|
||||
ret = TCM_INVALID_PARAMETER_LIST;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (lba + range > dev->transport->get_blocks(dev) + 1) {
|
||||
ret = TCM_ADDRESS_OUT_OF_RANGE;
|
||||
goto err;
|
||||
}
|
||||
|
||||
err = blkdev_issue_discard(ib_dev->ibd_bd, lba, range,
|
||||
GFP_KERNEL, 0);
|
||||
if (err < 0) {
|
||||
pr_err("blkdev_issue_discard() failed: %d\n",
|
||||
err);
|
||||
ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ptr += 16;
|
||||
size -= 16;
|
||||
}
|
||||
|
||||
err:
|
||||
transport_kunmap_data_sg(cmd);
|
||||
if (!ret)
|
||||
target_complete_cmd(cmd, GOOD);
|
||||
return ret;
|
||||
return sbc_execute_unmap(cmd, iblock_do_unmap, bdev);
|
||||
}
|
||||
|
||||
static sense_reason_t
|
||||
iblock_execute_write_same_unmap(struct se_cmd *cmd)
|
||||
{
|
||||
struct iblock_dev *ib_dev = IBLOCK_DEV(cmd->se_dev);
|
||||
int rc;
|
||||
struct block_device *bdev = IBLOCK_DEV(cmd->se_dev)->ibd_bd;
|
||||
sector_t lba = cmd->t_task_lba;
|
||||
sector_t nolb = sbc_get_write_same_sectors(cmd);
|
||||
int ret;
|
||||
|
||||
rc = blkdev_issue_discard(ib_dev->ibd_bd, cmd->t_task_lba,
|
||||
sbc_get_write_same_sectors(cmd), GFP_KERNEL, 0);
|
||||
if (rc < 0) {
|
||||
pr_warn("blkdev_issue_discard() failed: %d\n", rc);
|
||||
return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
|
||||
}
|
||||
ret = iblock_do_unmap(cmd, bdev, lba, nolb);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
target_complete_cmd(cmd, GOOD);
|
||||
return 0;
|
||||
|
|
|
@ -596,3 +596,88 @@ u32 sbc_get_device_type(struct se_device *dev)
|
|||
return TYPE_DISK;
|
||||
}
|
||||
EXPORT_SYMBOL(sbc_get_device_type);
|
||||
|
||||
sense_reason_t
|
||||
sbc_execute_unmap(struct se_cmd *cmd,
|
||||
sense_reason_t (*do_unmap_fn)(struct se_cmd *, void *,
|
||||
sector_t, sector_t),
|
||||
void *priv)
|
||||
{
|
||||
struct se_device *dev = cmd->se_dev;
|
||||
unsigned char *buf, *ptr = NULL;
|
||||
sector_t lba;
|
||||
int size;
|
||||
u32 range;
|
||||
sense_reason_t ret = 0;
|
||||
int dl, bd_dl;
|
||||
|
||||
/* We never set ANC_SUP */
|
||||
if (cmd->t_task_cdb[1])
|
||||
return TCM_INVALID_CDB_FIELD;
|
||||
|
||||
if (cmd->data_length == 0) {
|
||||
target_complete_cmd(cmd, SAM_STAT_GOOD);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cmd->data_length < 8) {
|
||||
pr_warn("UNMAP parameter list length %u too small\n",
|
||||
cmd->data_length);
|
||||
return TCM_PARAMETER_LIST_LENGTH_ERROR;
|
||||
}
|
||||
|
||||
buf = transport_kmap_data_sg(cmd);
|
||||
if (!buf)
|
||||
return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
|
||||
|
||||
dl = get_unaligned_be16(&buf[0]);
|
||||
bd_dl = get_unaligned_be16(&buf[2]);
|
||||
|
||||
size = cmd->data_length - 8;
|
||||
if (bd_dl > size)
|
||||
pr_warn("UNMAP parameter list length %u too small, ignoring bd_dl %u\n",
|
||||
cmd->data_length, bd_dl);
|
||||
else
|
||||
size = bd_dl;
|
||||
|
||||
if (size / 16 > dev->dev_attrib.max_unmap_block_desc_count) {
|
||||
ret = TCM_INVALID_PARAMETER_LIST;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* First UNMAP block descriptor starts at 8 byte offset */
|
||||
ptr = &buf[8];
|
||||
pr_debug("UNMAP: Sub: %s Using dl: %u bd_dl: %u size: %u"
|
||||
" ptr: %p\n", dev->transport->name, dl, bd_dl, size, ptr);
|
||||
|
||||
while (size >= 16) {
|
||||
lba = get_unaligned_be64(&ptr[0]);
|
||||
range = get_unaligned_be32(&ptr[8]);
|
||||
pr_debug("UNMAP: Using lba: %llu and range: %u\n",
|
||||
(unsigned long long)lba, range);
|
||||
|
||||
if (range > dev->dev_attrib.max_unmap_lba_count) {
|
||||
ret = TCM_INVALID_PARAMETER_LIST;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (lba + range > dev->transport->get_blocks(dev) + 1) {
|
||||
ret = TCM_ADDRESS_OUT_OF_RANGE;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = do_unmap_fn(cmd, priv, lba, range);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ptr += 16;
|
||||
size -= 16;
|
||||
}
|
||||
|
||||
err:
|
||||
transport_kunmap_data_sg(cmd);
|
||||
if (!ret)
|
||||
target_complete_cmd(cmd, GOOD);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(sbc_execute_unmap);
|
||||
|
|
|
@ -65,7 +65,6 @@ static void transport_complete_task_attr(struct se_cmd *cmd);
|
|||
static void transport_handle_queue_full(struct se_cmd *cmd,
|
||||
struct se_device *dev);
|
||||
static int transport_generic_get_mem(struct se_cmd *cmd);
|
||||
static int target_get_sess_cmd(struct se_session *, struct se_cmd *, bool);
|
||||
static void transport_put_cmd(struct se_cmd *cmd);
|
||||
static void target_complete_ok_work(struct work_struct *work);
|
||||
|
||||
|
@ -2179,7 +2178,7 @@ EXPORT_SYMBOL(transport_generic_free_cmd);
|
|||
* @se_cmd: command descriptor to add
|
||||
* @ack_kref: Signal that fabric will perform an ack target_put_sess_cmd()
|
||||
*/
|
||||
static int target_get_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd,
|
||||
int target_get_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd,
|
||||
bool ack_kref)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
@ -2208,6 +2207,7 @@ static int target_get_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd
|
|||
spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(target_get_sess_cmd);
|
||||
|
||||
static void target_release_cmd_kref(struct kref *kref)
|
||||
{
|
||||
|
@ -2765,8 +2765,13 @@ transport_send_check_condition_and_sense(struct se_cmd *cmd,
|
|||
/* CURRENT ERROR */
|
||||
buffer[0] = 0x70;
|
||||
buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
|
||||
/* ILLEGAL REQUEST */
|
||||
buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
|
||||
/*
|
||||
* Returning ILLEGAL REQUEST would cause immediate IO errors on
|
||||
* Solaris initiators. Returning NOT READY instead means the
|
||||
* operations will be retried a finite number of times and we
|
||||
* can survive intermittent errors.
|
||||
*/
|
||||
buffer[SPC_SENSE_KEY_OFFSET] = NOT_READY;
|
||||
/* LOGICAL UNIT COMMUNICATION FAILURE */
|
||||
buffer[SPC_ASC_KEY_OFFSET] = 0x08;
|
||||
break;
|
||||
|
|
|
@ -103,6 +103,13 @@ int ft_queue_data_in(struct se_cmd *se_cmd)
|
|||
use_sg = !(remaining % 4);
|
||||
|
||||
while (remaining) {
|
||||
struct fc_seq *seq = cmd->seq;
|
||||
|
||||
if (!seq) {
|
||||
pr_debug("%s: Command aborted, xid 0x%x\n",
|
||||
__func__, ep->xid);
|
||||
break;
|
||||
}
|
||||
if (!mem_len) {
|
||||
sg = sg_next(sg);
|
||||
mem_len = min((size_t)sg->length, remaining);
|
||||
|
@ -169,7 +176,7 @@ int ft_queue_data_in(struct se_cmd *se_cmd)
|
|||
f_ctl |= FC_FC_END_SEQ;
|
||||
fc_fill_fc_hdr(fp, FC_RCTL_DD_SOL_DATA, ep->did, ep->sid,
|
||||
FC_TYPE_FCP, f_ctl, fh_off);
|
||||
error = lport->tt.seq_send(lport, cmd->seq, fp);
|
||||
error = lport->tt.seq_send(lport, seq, fp);
|
||||
if (error) {
|
||||
/* XXX For now, initiator will retry */
|
||||
pr_err_ratelimited("%s: Failed to send frame %p, "
|
||||
|
|
|
@ -428,19 +428,12 @@ static int ft_prli(struct fc_rport_priv *rdata, u32 spp_len,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void ft_sess_rcu_free(struct rcu_head *rcu)
|
||||
{
|
||||
struct ft_sess *sess = container_of(rcu, struct ft_sess, rcu);
|
||||
|
||||
kfree(sess);
|
||||
}
|
||||
|
||||
static void ft_sess_free(struct kref *kref)
|
||||
{
|
||||
struct ft_sess *sess = container_of(kref, struct ft_sess, kref);
|
||||
|
||||
transport_deregister_session(sess->se_sess);
|
||||
call_rcu(&sess->rcu, ft_sess_rcu_free);
|
||||
kfree_rcu(sess, rcu);
|
||||
}
|
||||
|
||||
void ft_sess_put(struct ft_sess *sess)
|
||||
|
|
|
@ -66,11 +66,13 @@ enum {
|
|||
* TODO: debug and remove the workaround.
|
||||
*/
|
||||
enum {
|
||||
VHOST_SCSI_FEATURES = VHOST_FEATURES & (~VIRTIO_RING_F_EVENT_IDX)
|
||||
VHOST_SCSI_FEATURES = (VHOST_FEATURES & (~VIRTIO_RING_F_EVENT_IDX)) |
|
||||
(1ULL << VIRTIO_SCSI_F_HOTPLUG)
|
||||
};
|
||||
|
||||
#define VHOST_SCSI_MAX_TARGET 256
|
||||
#define VHOST_SCSI_MAX_VQ 128
|
||||
#define VHOST_SCSI_MAX_EVENT 128
|
||||
|
||||
struct vhost_scsi {
|
||||
/* Protected by vhost_scsi->dev.mutex */
|
||||
|
@ -82,6 +84,12 @@ struct vhost_scsi {
|
|||
|
||||
struct vhost_work vs_completion_work; /* cmd completion work item */
|
||||
struct llist_head vs_completion_list; /* cmd completion queue */
|
||||
|
||||
struct vhost_work vs_event_work; /* evt injection work item */
|
||||
struct llist_head vs_event_list; /* evt injection queue */
|
||||
|
||||
bool vs_events_missed; /* any missed events, protected by vq->mutex */
|
||||
int vs_events_nr; /* num of pending events, protected by vq->mutex */
|
||||
};
|
||||
|
||||
/* Local pointer to allocated TCM configfs fabric module */
|
||||
|
@ -349,6 +357,37 @@ static int tcm_vhost_queue_tm_rsp(struct se_cmd *se_cmd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void tcm_vhost_free_evt(struct vhost_scsi *vs, struct tcm_vhost_evt *evt)
|
||||
{
|
||||
vs->vs_events_nr--;
|
||||
kfree(evt);
|
||||
}
|
||||
|
||||
static struct tcm_vhost_evt *tcm_vhost_allocate_evt(struct vhost_scsi *vs,
|
||||
u32 event, u32 reason)
|
||||
{
|
||||
struct vhost_virtqueue *vq = &vs->vqs[VHOST_SCSI_VQ_EVT];
|
||||
struct tcm_vhost_evt *evt;
|
||||
|
||||
if (vs->vs_events_nr > VHOST_SCSI_MAX_EVENT) {
|
||||
vs->vs_events_missed = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
evt = kzalloc(sizeof(*evt), GFP_KERNEL);
|
||||
if (!evt) {
|
||||
vq_err(vq, "Failed to allocate tcm_vhost_evt\n");
|
||||
vs->vs_events_missed = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
evt->event.event = event;
|
||||
evt->event.reason = reason;
|
||||
vs->vs_events_nr++;
|
||||
|
||||
return evt;
|
||||
}
|
||||
|
||||
static void vhost_scsi_free_cmd(struct tcm_vhost_cmd *tv_cmd)
|
||||
{
|
||||
struct se_cmd *se_cmd = &tv_cmd->tvc_se_cmd;
|
||||
|
@ -367,6 +406,75 @@ static void vhost_scsi_free_cmd(struct tcm_vhost_cmd *tv_cmd)
|
|||
kfree(tv_cmd);
|
||||
}
|
||||
|
||||
static void tcm_vhost_do_evt_work(struct vhost_scsi *vs,
|
||||
struct tcm_vhost_evt *evt)
|
||||
{
|
||||
struct vhost_virtqueue *vq = &vs->vqs[VHOST_SCSI_VQ_EVT];
|
||||
struct virtio_scsi_event *event = &evt->event;
|
||||
struct virtio_scsi_event __user *eventp;
|
||||
unsigned out, in;
|
||||
int head, ret;
|
||||
|
||||
if (!vq->private_data) {
|
||||
vs->vs_events_missed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
again:
|
||||
vhost_disable_notify(&vs->dev, vq);
|
||||
head = vhost_get_vq_desc(&vs->dev, vq, vq->iov,
|
||||
ARRAY_SIZE(vq->iov), &out, &in,
|
||||
NULL, NULL);
|
||||
if (head < 0) {
|
||||
vs->vs_events_missed = true;
|
||||
return;
|
||||
}
|
||||
if (head == vq->num) {
|
||||
if (vhost_enable_notify(&vs->dev, vq))
|
||||
goto again;
|
||||
vs->vs_events_missed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((vq->iov[out].iov_len != sizeof(struct virtio_scsi_event))) {
|
||||
vq_err(vq, "Expecting virtio_scsi_event, got %zu bytes\n",
|
||||
vq->iov[out].iov_len);
|
||||
vs->vs_events_missed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (vs->vs_events_missed) {
|
||||
event->event |= VIRTIO_SCSI_T_EVENTS_MISSED;
|
||||
vs->vs_events_missed = false;
|
||||
}
|
||||
|
||||
eventp = vq->iov[out].iov_base;
|
||||
ret = __copy_to_user(eventp, event, sizeof(*event));
|
||||
if (!ret)
|
||||
vhost_add_used_and_signal(&vs->dev, vq, head, 0);
|
||||
else
|
||||
vq_err(vq, "Faulted on tcm_vhost_send_event\n");
|
||||
}
|
||||
|
||||
static void tcm_vhost_evt_work(struct vhost_work *work)
|
||||
{
|
||||
struct vhost_scsi *vs = container_of(work, struct vhost_scsi,
|
||||
vs_event_work);
|
||||
struct vhost_virtqueue *vq = &vs->vqs[VHOST_SCSI_VQ_EVT];
|
||||
struct tcm_vhost_evt *evt;
|
||||
struct llist_node *llnode;
|
||||
|
||||
mutex_lock(&vq->mutex);
|
||||
llnode = llist_del_all(&vs->vs_event_list);
|
||||
while (llnode) {
|
||||
evt = llist_entry(llnode, struct tcm_vhost_evt, list);
|
||||
llnode = llist_next(llnode);
|
||||
tcm_vhost_do_evt_work(vs, evt);
|
||||
tcm_vhost_free_evt(vs, evt);
|
||||
}
|
||||
mutex_unlock(&vq->mutex);
|
||||
}
|
||||
|
||||
/* Fill in status and signal that we are done processing this command
|
||||
*
|
||||
* This is scheduled in the vhost work queue so we are called with the owner
|
||||
|
@ -777,9 +885,46 @@ static void vhost_scsi_ctl_handle_kick(struct vhost_work *work)
|
|||
pr_debug("%s: The handling func for control queue.\n", __func__);
|
||||
}
|
||||
|
||||
static void tcm_vhost_send_evt(struct vhost_scsi *vs, struct tcm_vhost_tpg *tpg,
|
||||
struct se_lun *lun, u32 event, u32 reason)
|
||||
{
|
||||
struct tcm_vhost_evt *evt;
|
||||
|
||||
evt = tcm_vhost_allocate_evt(vs, event, reason);
|
||||
if (!evt)
|
||||
return;
|
||||
|
||||
if (tpg && lun) {
|
||||
/* TODO: share lun setup code with virtio-scsi.ko */
|
||||
/*
|
||||
* Note: evt->event is zeroed when we allocate it and
|
||||
* lun[4-7] need to be zero according to virtio-scsi spec.
|
||||
*/
|
||||
evt->event.lun[0] = 0x01;
|
||||
evt->event.lun[1] = tpg->tport_tpgt & 0xFF;
|
||||
if (lun->unpacked_lun >= 256)
|
||||
evt->event.lun[2] = lun->unpacked_lun >> 8 | 0x40 ;
|
||||
evt->event.lun[3] = lun->unpacked_lun & 0xFF;
|
||||
}
|
||||
|
||||
llist_add(&evt->list, &vs->vs_event_list);
|
||||
vhost_work_queue(&vs->dev, &vs->vs_event_work);
|
||||
}
|
||||
|
||||
static void vhost_scsi_evt_handle_kick(struct vhost_work *work)
|
||||
{
|
||||
pr_debug("%s: The handling func for event queue.\n", __func__);
|
||||
struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue,
|
||||
poll.work);
|
||||
struct vhost_scsi *vs = container_of(vq->dev, struct vhost_scsi, dev);
|
||||
|
||||
mutex_lock(&vq->mutex);
|
||||
if (!vq->private_data)
|
||||
goto out;
|
||||
|
||||
if (vs->vs_events_missed)
|
||||
tcm_vhost_send_evt(vs, NULL, NULL, VIRTIO_SCSI_T_NO_EVENT, 0);
|
||||
out:
|
||||
mutex_unlock(&vq->mutex);
|
||||
}
|
||||
|
||||
static void vhost_scsi_handle_kick(struct vhost_work *work)
|
||||
|
@ -803,11 +948,15 @@ static void vhost_scsi_flush(struct vhost_scsi *vs)
|
|||
for (i = 0; i < VHOST_SCSI_MAX_VQ; i++)
|
||||
vhost_scsi_flush_vq(vs, i);
|
||||
vhost_work_flush(&vs->dev, &vs->vs_completion_work);
|
||||
vhost_work_flush(&vs->dev, &vs->vs_event_work);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from vhost_scsi_ioctl() context to walk the list of available
|
||||
* tcm_vhost_tpg with an active struct tcm_vhost_nexus
|
||||
*
|
||||
* The lock nesting rule is:
|
||||
* tcm_vhost_mutex -> vs->dev.mutex -> tpg->tv_tpg_mutex -> vq->mutex
|
||||
*/
|
||||
static int vhost_scsi_set_endpoint(
|
||||
struct vhost_scsi *vs,
|
||||
|
@ -820,26 +969,27 @@ static int vhost_scsi_set_endpoint(
|
|||
int index, ret, i, len;
|
||||
bool match = false;
|
||||
|
||||
mutex_lock(&tcm_vhost_mutex);
|
||||
mutex_lock(&vs->dev.mutex);
|
||||
|
||||
/* Verify that ring has been setup correctly. */
|
||||
for (index = 0; index < vs->dev.nvqs; ++index) {
|
||||
/* Verify that ring has been setup correctly. */
|
||||
if (!vhost_vq_access_ok(&vs->vqs[index])) {
|
||||
mutex_unlock(&vs->dev.mutex);
|
||||
return -EFAULT;
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
len = sizeof(vs_tpg[0]) * VHOST_SCSI_MAX_TARGET;
|
||||
vs_tpg = kzalloc(len, GFP_KERNEL);
|
||||
if (!vs_tpg) {
|
||||
mutex_unlock(&vs->dev.mutex);
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (vs->vs_tpg)
|
||||
memcpy(vs_tpg, vs->vs_tpg, len);
|
||||
|
||||
mutex_lock(&tcm_vhost_mutex);
|
||||
list_for_each_entry(tv_tpg, &tcm_vhost_list, tv_tpg_list) {
|
||||
mutex_lock(&tv_tpg->tv_tpg_mutex);
|
||||
if (!tv_tpg->tpg_nexus) {
|
||||
|
@ -854,20 +1004,19 @@ static int vhost_scsi_set_endpoint(
|
|||
|
||||
if (!strcmp(tv_tport->tport_name, t->vhost_wwpn)) {
|
||||
if (vs->vs_tpg && vs->vs_tpg[tv_tpg->tport_tpgt]) {
|
||||
mutex_unlock(&tv_tpg->tv_tpg_mutex);
|
||||
mutex_unlock(&tcm_vhost_mutex);
|
||||
mutex_unlock(&vs->dev.mutex);
|
||||
kfree(vs_tpg);
|
||||
return -EEXIST;
|
||||
mutex_unlock(&tv_tpg->tv_tpg_mutex);
|
||||
ret = -EEXIST;
|
||||
goto out;
|
||||
}
|
||||
tv_tpg->tv_tpg_vhost_count++;
|
||||
tv_tpg->vhost_scsi = vs;
|
||||
vs_tpg[tv_tpg->tport_tpgt] = tv_tpg;
|
||||
smp_mb__after_atomic_inc();
|
||||
match = true;
|
||||
}
|
||||
mutex_unlock(&tv_tpg->tv_tpg_mutex);
|
||||
}
|
||||
mutex_unlock(&tcm_vhost_mutex);
|
||||
|
||||
if (match) {
|
||||
memcpy(vs->vs_vhost_wwpn, t->vhost_wwpn,
|
||||
|
@ -893,7 +1042,9 @@ static int vhost_scsi_set_endpoint(
|
|||
kfree(vs->vs_tpg);
|
||||
vs->vs_tpg = vs_tpg;
|
||||
|
||||
out:
|
||||
mutex_unlock(&vs->dev.mutex);
|
||||
mutex_unlock(&tcm_vhost_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -908,6 +1059,7 @@ static int vhost_scsi_clear_endpoint(
|
|||
int index, ret, i;
|
||||
u8 target;
|
||||
|
||||
mutex_lock(&tcm_vhost_mutex);
|
||||
mutex_lock(&vs->dev.mutex);
|
||||
/* Verify that ring has been setup correctly. */
|
||||
for (index = 0; index < vs->dev.nvqs; ++index) {
|
||||
|
@ -918,8 +1070,8 @@ static int vhost_scsi_clear_endpoint(
|
|||
}
|
||||
|
||||
if (!vs->vs_tpg) {
|
||||
mutex_unlock(&vs->dev.mutex);
|
||||
return 0;
|
||||
ret = 0;
|
||||
goto err_dev;
|
||||
}
|
||||
|
||||
for (i = 0; i < VHOST_SCSI_MAX_TARGET; i++) {
|
||||
|
@ -944,6 +1096,7 @@ static int vhost_scsi_clear_endpoint(
|
|||
goto err_tpg;
|
||||
}
|
||||
tv_tpg->tv_tpg_vhost_count--;
|
||||
tv_tpg->vhost_scsi = NULL;
|
||||
vs->vs_tpg[target] = NULL;
|
||||
match = true;
|
||||
mutex_unlock(&tv_tpg->tv_tpg_mutex);
|
||||
|
@ -964,14 +1117,16 @@ static int vhost_scsi_clear_endpoint(
|
|||
vhost_scsi_flush(vs);
|
||||
kfree(vs->vs_tpg);
|
||||
vs->vs_tpg = NULL;
|
||||
WARN_ON(vs->vs_events_nr);
|
||||
mutex_unlock(&vs->dev.mutex);
|
||||
|
||||
mutex_unlock(&tcm_vhost_mutex);
|
||||
return 0;
|
||||
|
||||
err_tpg:
|
||||
mutex_unlock(&tv_tpg->tv_tpg_mutex);
|
||||
err_dev:
|
||||
mutex_unlock(&vs->dev.mutex);
|
||||
mutex_unlock(&tcm_vhost_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1003,6 +1158,10 @@ static int vhost_scsi_open(struct inode *inode, struct file *f)
|
|||
return -ENOMEM;
|
||||
|
||||
vhost_work_init(&s->vs_completion_work, vhost_scsi_complete_cmd_work);
|
||||
vhost_work_init(&s->vs_event_work, tcm_vhost_evt_work);
|
||||
|
||||
s->vs_events_nr = 0;
|
||||
s->vs_events_missed = false;
|
||||
|
||||
s->vqs[VHOST_SCSI_VQ_CTL].handle_kick = vhost_scsi_ctl_handle_kick;
|
||||
s->vqs[VHOST_SCSI_VQ_EVT].handle_kick = vhost_scsi_evt_handle_kick;
|
||||
|
@ -1029,6 +1188,8 @@ static int vhost_scsi_release(struct inode *inode, struct file *f)
|
|||
vhost_scsi_clear_endpoint(s, &t);
|
||||
vhost_dev_stop(&s->dev);
|
||||
vhost_dev_cleanup(&s->dev, false);
|
||||
/* Jobs can re-queue themselves in evt kick handler. Do extra flush. */
|
||||
vhost_scsi_flush(s);
|
||||
kfree(s);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1040,8 +1201,11 @@ static long vhost_scsi_ioctl(struct file *f, unsigned int ioctl,
|
|||
struct vhost_scsi_target backend;
|
||||
void __user *argp = (void __user *)arg;
|
||||
u64 __user *featurep = argp;
|
||||
u32 __user *eventsp = argp;
|
||||
u32 events_missed;
|
||||
u64 features;
|
||||
int r, abi_version = VHOST_SCSI_ABI_VERSION;
|
||||
struct vhost_virtqueue *vq = &vs->vqs[VHOST_SCSI_VQ_EVT];
|
||||
|
||||
switch (ioctl) {
|
||||
case VHOST_SCSI_SET_ENDPOINT:
|
||||
|
@ -1062,6 +1226,20 @@ static long vhost_scsi_ioctl(struct file *f, unsigned int ioctl,
|
|||
if (copy_to_user(argp, &abi_version, sizeof abi_version))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
case VHOST_SCSI_SET_EVENTS_MISSED:
|
||||
if (get_user(events_missed, eventsp))
|
||||
return -EFAULT;
|
||||
mutex_lock(&vq->mutex);
|
||||
vs->vs_events_missed = events_missed;
|
||||
mutex_unlock(&vq->mutex);
|
||||
return 0;
|
||||
case VHOST_SCSI_GET_EVENTS_MISSED:
|
||||
mutex_lock(&vq->mutex);
|
||||
events_missed = vs->vs_events_missed;
|
||||
mutex_unlock(&vq->mutex);
|
||||
if (put_user(events_missed, eventsp))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
case VHOST_GET_FEATURES:
|
||||
features = VHOST_SCSI_FEATURES;
|
||||
if (copy_to_user(featurep, &features, sizeof features))
|
||||
|
@ -1133,28 +1311,80 @@ static char *tcm_vhost_dump_proto_id(struct tcm_vhost_tport *tport)
|
|||
return "Unknown";
|
||||
}
|
||||
|
||||
static void tcm_vhost_do_plug(struct tcm_vhost_tpg *tpg,
|
||||
struct se_lun *lun, bool plug)
|
||||
{
|
||||
|
||||
struct vhost_scsi *vs = tpg->vhost_scsi;
|
||||
struct vhost_virtqueue *vq;
|
||||
u32 reason;
|
||||
|
||||
if (!vs)
|
||||
return;
|
||||
|
||||
mutex_lock(&vs->dev.mutex);
|
||||
if (!vhost_has_feature(&vs->dev, VIRTIO_SCSI_F_HOTPLUG)) {
|
||||
mutex_unlock(&vs->dev.mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (plug)
|
||||
reason = VIRTIO_SCSI_EVT_RESET_RESCAN;
|
||||
else
|
||||
reason = VIRTIO_SCSI_EVT_RESET_REMOVED;
|
||||
|
||||
vq = &vs->vqs[VHOST_SCSI_VQ_EVT];
|
||||
mutex_lock(&vq->mutex);
|
||||
tcm_vhost_send_evt(vs, tpg, lun,
|
||||
VIRTIO_SCSI_T_TRANSPORT_RESET, reason);
|
||||
mutex_unlock(&vq->mutex);
|
||||
mutex_unlock(&vs->dev.mutex);
|
||||
}
|
||||
|
||||
static void tcm_vhost_hotplug(struct tcm_vhost_tpg *tpg, struct se_lun *lun)
|
||||
{
|
||||
tcm_vhost_do_plug(tpg, lun, true);
|
||||
}
|
||||
|
||||
static void tcm_vhost_hotunplug(struct tcm_vhost_tpg *tpg, struct se_lun *lun)
|
||||
{
|
||||
tcm_vhost_do_plug(tpg, lun, false);
|
||||
}
|
||||
|
||||
static int tcm_vhost_port_link(struct se_portal_group *se_tpg,
|
||||
struct se_lun *lun)
|
||||
{
|
||||
struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg,
|
||||
struct tcm_vhost_tpg, se_tpg);
|
||||
|
||||
mutex_lock(&tcm_vhost_mutex);
|
||||
|
||||
mutex_lock(&tv_tpg->tv_tpg_mutex);
|
||||
tv_tpg->tv_tpg_port_count++;
|
||||
mutex_unlock(&tv_tpg->tv_tpg_mutex);
|
||||
|
||||
tcm_vhost_hotplug(tv_tpg, lun);
|
||||
|
||||
mutex_unlock(&tcm_vhost_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tcm_vhost_port_unlink(struct se_portal_group *se_tpg,
|
||||
struct se_lun *se_lun)
|
||||
struct se_lun *lun)
|
||||
{
|
||||
struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg,
|
||||
struct tcm_vhost_tpg, se_tpg);
|
||||
|
||||
mutex_lock(&tcm_vhost_mutex);
|
||||
|
||||
mutex_lock(&tv_tpg->tv_tpg_mutex);
|
||||
tv_tpg->tv_tpg_port_count--;
|
||||
mutex_unlock(&tv_tpg->tv_tpg_mutex);
|
||||
|
||||
tcm_vhost_hotunplug(tv_tpg, lun);
|
||||
|
||||
mutex_unlock(&tcm_vhost_mutex);
|
||||
}
|
||||
|
||||
static struct se_node_acl *tcm_vhost_make_nodeacl(
|
||||
|
|
|
@ -53,6 +53,7 @@ struct tcm_vhost_nacl {
|
|||
struct se_node_acl se_node_acl;
|
||||
};
|
||||
|
||||
struct vhost_scsi;
|
||||
struct tcm_vhost_tpg {
|
||||
/* Vhost port target portal group tag for TCM */
|
||||
u16 tport_tpgt;
|
||||
|
@ -70,6 +71,8 @@ struct tcm_vhost_tpg {
|
|||
struct tcm_vhost_tport *tport;
|
||||
/* Returned by tcm_vhost_make_tpg() */
|
||||
struct se_portal_group se_tpg;
|
||||
/* Pointer back to vhost_scsi, protected by tv_tpg_mutex */
|
||||
struct vhost_scsi *vhost_scsi;
|
||||
};
|
||||
|
||||
struct tcm_vhost_tport {
|
||||
|
@ -83,6 +86,13 @@ struct tcm_vhost_tport {
|
|||
struct se_wwn tport_wwn;
|
||||
};
|
||||
|
||||
struct tcm_vhost_evt {
|
||||
/* event to be sent to guest */
|
||||
struct virtio_scsi_event event;
|
||||
/* event list, serviced from vhost worker thread */
|
||||
struct llist_node list;
|
||||
};
|
||||
|
||||
/*
|
||||
* As per request from MST, keep TCM_VHOST related ioctl defines out of
|
||||
* linux/vhost.h (user-space) for now..
|
||||
|
@ -113,3 +123,6 @@ struct vhost_scsi_target {
|
|||
#define VHOST_SCSI_CLEAR_ENDPOINT _IOW(VHOST_VIRTIO, 0x41, struct vhost_scsi_target)
|
||||
/* Changing this breaks userspace. */
|
||||
#define VHOST_SCSI_GET_ABI_VERSION _IOW(VHOST_VIRTIO, 0x42, int)
|
||||
/* Set and get the events missed flag */
|
||||
#define VHOST_SCSI_SET_EVENTS_MISSED _IOW(VHOST_VIRTIO, 0x43, __u32)
|
||||
#define VHOST_SCSI_GET_EVENTS_MISSED _IOW(VHOST_VIRTIO, 0x44, __u32)
|
||||
|
|
83
include/target/iscsi/iscsi_transport.h
Normal file
83
include/target/iscsi/iscsi_transport.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/list.h>
|
||||
#include "../../../drivers/target/iscsi/iscsi_target_core.h"
|
||||
|
||||
struct iscsit_transport {
|
||||
#define ISCSIT_TRANSPORT_NAME 16
|
||||
char name[ISCSIT_TRANSPORT_NAME];
|
||||
int transport_type;
|
||||
struct module *owner;
|
||||
struct list_head t_node;
|
||||
int (*iscsit_setup_np)(struct iscsi_np *, struct __kernel_sockaddr_storage *);
|
||||
int (*iscsit_accept_np)(struct iscsi_np *, struct iscsi_conn *);
|
||||
void (*iscsit_free_np)(struct iscsi_np *);
|
||||
void (*iscsit_free_conn)(struct iscsi_conn *);
|
||||
struct iscsi_cmd *(*iscsit_alloc_cmd)(struct iscsi_conn *, gfp_t);
|
||||
int (*iscsit_get_login_rx)(struct iscsi_conn *, struct iscsi_login *);
|
||||
int (*iscsit_put_login_tx)(struct iscsi_conn *, struct iscsi_login *, u32);
|
||||
int (*iscsit_immediate_queue)(struct iscsi_conn *, struct iscsi_cmd *, int);
|
||||
int (*iscsit_response_queue)(struct iscsi_conn *, struct iscsi_cmd *, int);
|
||||
int (*iscsit_get_dataout)(struct iscsi_conn *, struct iscsi_cmd *, bool);
|
||||
int (*iscsit_queue_data_in)(struct iscsi_conn *, struct iscsi_cmd *);
|
||||
int (*iscsit_queue_status)(struct iscsi_conn *, struct iscsi_cmd *);
|
||||
};
|
||||
|
||||
/*
|
||||
* From iscsi_target_transport.c
|
||||
*/
|
||||
|
||||
extern int iscsit_register_transport(struct iscsit_transport *);
|
||||
extern void iscsit_unregister_transport(struct iscsit_transport *);
|
||||
extern struct iscsit_transport *iscsit_get_transport(int);
|
||||
extern void iscsit_put_transport(struct iscsit_transport *);
|
||||
|
||||
/*
|
||||
* From iscsi_target.c
|
||||
*/
|
||||
extern int iscsit_add_reject_from_cmd(u8, int, int, unsigned char *,
|
||||
struct iscsi_cmd *);
|
||||
extern int iscsit_setup_scsi_cmd(struct iscsi_conn *, struct iscsi_cmd *,
|
||||
unsigned char *);
|
||||
extern void iscsit_set_unsoliticed_dataout(struct iscsi_cmd *);
|
||||
extern int iscsit_process_scsi_cmd(struct iscsi_conn *, struct iscsi_cmd *,
|
||||
struct iscsi_scsi_req *);
|
||||
extern int iscsit_check_dataout_hdr(struct iscsi_conn *, unsigned char *,
|
||||
struct iscsi_cmd **);
|
||||
extern int iscsit_check_dataout_payload(struct iscsi_cmd *, struct iscsi_data *,
|
||||
bool);
|
||||
extern int iscsit_handle_nop_out(struct iscsi_conn *, struct iscsi_cmd *,
|
||||
unsigned char *);
|
||||
extern int iscsit_handle_logout_cmd(struct iscsi_conn *, struct iscsi_cmd *,
|
||||
unsigned char *);
|
||||
extern int iscsit_handle_task_mgt_cmd(struct iscsi_conn *, struct iscsi_cmd *,
|
||||
unsigned char *);
|
||||
extern void iscsit_build_rsp_pdu(struct iscsi_cmd *, struct iscsi_conn *,
|
||||
bool, struct iscsi_scsi_rsp *);
|
||||
extern void iscsit_build_nopin_rsp(struct iscsi_cmd *, struct iscsi_conn *,
|
||||
struct iscsi_nopin *, bool);
|
||||
extern void iscsit_build_task_mgt_rsp(struct iscsi_cmd *, struct iscsi_conn *,
|
||||
struct iscsi_tm_rsp *);
|
||||
extern void iscsit_build_reject(struct iscsi_cmd *, struct iscsi_conn *,
|
||||
struct iscsi_reject *);
|
||||
extern int iscsit_build_logout_rsp(struct iscsi_cmd *, struct iscsi_conn *,
|
||||
struct iscsi_logout_rsp *);
|
||||
extern int iscsit_logout_post_handler(struct iscsi_cmd *, struct iscsi_conn *);
|
||||
/*
|
||||
* From iscsi_target_device.c
|
||||
*/
|
||||
extern void iscsit_increment_maxcmdsn(struct iscsi_cmd *, struct iscsi_session *);
|
||||
/*
|
||||
* From iscsi_target_erl1.c
|
||||
*/
|
||||
extern void iscsit_stop_dataout_timer(struct iscsi_cmd *);
|
||||
|
||||
/*
|
||||
* From iscsi_target_tmr.c
|
||||
*/
|
||||
extern int iscsit_tmr_post_handler(struct iscsi_cmd *, struct iscsi_conn *);
|
||||
|
||||
/*
|
||||
* From iscsi_target_util.c
|
||||
*/
|
||||
extern struct iscsi_cmd *iscsit_allocate_cmd(struct iscsi_conn *, gfp_t);
|
||||
extern int iscsit_sequence_cmd(struct iscsi_conn *, struct iscsi_cmd *, __be32);
|
|
@ -60,6 +60,10 @@ sense_reason_t sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops);
|
|||
u32 sbc_get_device_rev(struct se_device *dev);
|
||||
u32 sbc_get_device_type(struct se_device *dev);
|
||||
sector_t sbc_get_write_same_sectors(struct se_cmd *cmd);
|
||||
sense_reason_t sbc_execute_unmap(struct se_cmd *cmd,
|
||||
sense_reason_t (*do_unmap_fn)(struct se_cmd *cmd, void *priv,
|
||||
sector_t lba, sector_t nolb),
|
||||
void *priv);
|
||||
|
||||
void transport_set_vpd_proto_id(struct t10_vpd *, unsigned char *);
|
||||
int transport_set_vpd_assoc(struct t10_vpd *, unsigned char *);
|
||||
|
|
|
@ -120,7 +120,7 @@ bool transport_wait_for_tasks(struct se_cmd *);
|
|||
int transport_check_aborted_status(struct se_cmd *, int);
|
||||
int transport_send_check_condition_and_sense(struct se_cmd *,
|
||||
sense_reason_t, int);
|
||||
|
||||
int target_get_sess_cmd(struct se_session *, struct se_cmd *, bool);
|
||||
int target_put_sess_cmd(struct se_session *, struct se_cmd *);
|
||||
void target_sess_cmd_list_set_waiting(struct se_session *);
|
||||
void target_wait_for_sess_cmds(struct se_session *, int);
|
||||
|
|
Loading…
Reference in a new issue