Merge branch 'for-next-merge' of git://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending

Pull SCSI target update from Nicholas Bellinger:
 "The highlights this round include:

   - Add fileio support for WRITE_SAME w/ UNMAP=1 discard (asias)
   - Add fileio support for UNMAP discard (asias)
   - Add tcm_vhost hotplug support to work with upstream QEMU
     vhost-scsi-pci code (asias + mst)
   - Check for aborted sequence in tcm_fc response path (mdr)
   - Add initial iscsit_transport support into iscsi-target code (nab)
   - Refactor iscsi-target RX PDU logic + export request PDU handling
     (nab)
   - Refactor iscsi-target TX queue logic + export response PDU creation
     (nab)
   - Add new iSCSI Extentions for RDMA (ISER) target driver (Or + nab)

  The biggest changes revolve around iscsi-target refactoring in order
  to support the iser-target driver.  This includes the conversion of
  the iscsi-target data-path to use modern se_cmd->cmd_kref counting,
  and allowing transport independent aspects of RX/TX PDU
  request/response handling be shared across existing traditional
  iscsi-target code, and the new iser-target code.

  Thanks to Or Gerlitz + Mellanox for supporting the iser-target
  development effort!"

* 'for-next-merge' of git://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending: (25 commits)
  iser-target: Add iSCSI Extensions for RDMA (iSER) target driver
  tcm_vhost: Enable VIRTIO_SCSI_F_HOTPLUG
  tcm_vhost: Add ioctl to get and set events missed flag
  tcm_vhost: Add hotplug/hotunplug support
  tcm_vhost: Refactor the lock nesting rule
  tcm_fc: Check for aborted sequence
  iscsi-target: Add iser network portal attribute
  iscsi-target: Refactor TX queue logic + export response PDU creation
  iscsi-target: Refactor RX PDU logic + export request PDU handling
  iscsi-target: Add per transport iscsi_cmd alloc/free
  iscsi-target: Add iser-target parameter keys + setup during login
  iscsi-target: Initial traditional TCP conversion to iscsit_transport
  iscsi-target: Add iscsit_transport API template
  target: Add export of target_get_sess_cmd symbol
  target: Change default sense key of NOT_READY
  target/file: Set is_nonrot attribute
  target: Add sbc_execute_unmap() helper
  target/iblock: Add iblock_do_unmap() helper
  target/file: Add fd_do_unmap() helper
  target/file: Add UNMAP emulation support
  ...
This commit is contained in:
Linus Torvalds 2013-04-30 13:14:57 -07:00
commit 6da6dc2380
39 changed files with 4477 additions and 1009 deletions

View file

@ -59,5 +59,6 @@ source "drivers/infiniband/ulp/srp/Kconfig"
source "drivers/infiniband/ulp/srpt/Kconfig" source "drivers/infiniband/ulp/srpt/Kconfig"
source "drivers/infiniband/ulp/iser/Kconfig" source "drivers/infiniband/ulp/iser/Kconfig"
source "drivers/infiniband/ulp/isert/Kconfig"
endif # INFINIBAND endif # INFINIBAND

View file

@ -13,3 +13,4 @@ obj-$(CONFIG_INFINIBAND_IPOIB) += ulp/ipoib/
obj-$(CONFIG_INFINIBAND_SRP) += ulp/srp/ obj-$(CONFIG_INFINIBAND_SRP) += ulp/srp/
obj-$(CONFIG_INFINIBAND_SRPT) += ulp/srpt/ obj-$(CONFIG_INFINIBAND_SRPT) += ulp/srpt/
obj-$(CONFIG_INFINIBAND_ISER) += ulp/iser/ obj-$(CONFIG_INFINIBAND_ISER) += ulp/iser/
obj-$(CONFIG_INFINIBAND_ISERT) += ulp/isert/

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

View file

@ -0,0 +1,2 @@
ccflags-y := -Idrivers/target -Idrivers/target/iscsi
obj-$(CONFIG_INFINIBAND_ISERT) += ib_isert.o

File diff suppressed because it is too large Load diff

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

View 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

View file

@ -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->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, static inline int qlt_get_fcp_task_attr(struct scsi_qla_host *vha,
uint8_t task_codes) uint8_t task_codes)
{ {

View file

@ -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_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *);
extern void qlt_free_mcmd(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_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_async_event(uint16_t, struct scsi_qla_host *, uint16_t *);
extern void qlt_enable_vha(struct scsi_qla_host *); extern void qlt_enable_vha(struct scsi_qla_host *);
extern void qlt_vport_create(struct scsi_qla_host *, struct qla_hw_data *); extern void qlt_vport_create(struct scsi_qla_host *, struct qla_hw_data *);

View file

@ -15,6 +15,7 @@ iscsi_target_mod-y += iscsi_target_parameters.o \
iscsi_target_util.o \ iscsi_target_util.o \
iscsi_target.o \ iscsi_target.o \
iscsi_target_configfs.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 obj-$(CONFIG_ISCSI_TARGET) += iscsi_target_mod.o

File diff suppressed because it is too large Load diff

View file

@ -16,11 +16,12 @@ extern int iscsit_reset_np_thread(struct iscsi_np *, struct iscsi_tpg_np *,
struct iscsi_portal_group *); struct iscsi_portal_group *);
extern int iscsit_del_np(struct iscsi_np *); extern int iscsit_del_np(struct iscsi_np *);
extern int iscsit_add_reject_from_cmd(u8, int, int, unsigned char *, struct iscsi_cmd *); 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_closesession(struct iscsi_cmd *, struct iscsi_conn *);
extern int iscsit_logout_closeconnection(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_logout_removeconnforrecovery(struct iscsi_cmd *, struct iscsi_conn *);
extern int iscsit_send_async_msg(struct iscsi_conn *, u16, u8, u8); 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 void iscsit_thread_get_cpumask(struct iscsi_conn *);
extern int iscsi_target_tx_thread(void *); extern int iscsi_target_tx_thread(void *);
extern int iscsi_target_rx_thread(void *); extern int iscsi_target_rx_thread(void *);

View file

@ -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( static void chap_gen_challenge(
struct iscsi_conn *conn, struct iscsi_conn *conn,
int caller, int caller,
@ -86,7 +60,7 @@ static void chap_gen_challenge(
memset(challenge_asciihex, 0, CHAP_CHALLENGE_LENGTH * 2 + 1); 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_binaryhex_to_asciihex(challenge_asciihex, chap->challenge,
CHAP_CHALLENGE_LENGTH); CHAP_CHALLENGE_LENGTH);
/* /*

View file

@ -27,6 +27,7 @@
#include <target/target_core_fabric_configfs.h> #include <target/target_core_fabric_configfs.h>
#include <target/target_core_configfs.h> #include <target/target_core_configfs.h>
#include <target/configfs_macros.h> #include <target/configfs_macros.h>
#include <target/iscsi/iscsi_transport.h>
#include "iscsi_target_core.h" #include "iscsi_target_core.h"
#include "iscsi_target_parameters.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); 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[] = { static struct configfs_attribute *lio_target_portal_attrs[] = {
&lio_target_np_sctp.attr, &lio_target_np_sctp.attr,
&lio_target_np_iser.attr,
NULL, 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); struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
cmd->i_state = ISTATE_SEND_DATAIN; 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; return 0;
} }
static int lio_write_pending(struct se_cmd *se_cmd) 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_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
struct iscsi_conn *conn = cmd->conn;
if (!cmd->immediate_data && !cmd->unsolicited_data) 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; 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); struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
cmd->i_state = ISTATE_SEND_STATUS; 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; 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); 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) static void lio_release_cmd(struct se_cmd *se_cmd)
{ {
struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_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 */ /* 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_alloc_fabric_acl = &lio_tpg_alloc_fabric_acl;
fabric->tf_ops.tpg_release_fabric_acl = &lio_tpg_release_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.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.release_cmd = &lio_release_cmd;
fabric->tf_ops.shutdown_session = &lio_tpg_shutdown_session; fabric->tf_ops.shutdown_session = &lio_tpg_shutdown_session;
fabric->tf_ops.close_session = &lio_tpg_close_session; fabric->tf_ops.close_session = &lio_tpg_close_session;

View file

@ -60,7 +60,7 @@
#define ISCSI_IOV_DATA_BUFFER 5 #define ISCSI_IOV_DATA_BUFFER 5
enum tpg_np_network_transport_table { enum iscsit_transport_type {
ISCSI_TCP = 0, ISCSI_TCP = 0,
ISCSI_SCTP_TCP = 1, ISCSI_SCTP_TCP = 1,
ISCSI_SCTP_UDP = 2, ISCSI_SCTP_UDP = 2,
@ -244,6 +244,11 @@ struct iscsi_conn_ops {
u8 IFMarker; /* [0,1] == [No,Yes] */ u8 IFMarker; /* [0,1] == [No,Yes] */
u32 OFMarkInt; /* [1..65535] */ u32 OFMarkInt; /* [1..65535] */
u32 IFMarkInt; /* [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 { struct iscsi_sess_ops {
@ -265,6 +270,10 @@ struct iscsi_sess_ops {
u8 DataSequenceInOrder; /* [0,1] == [No,Yes] */ u8 DataSequenceInOrder; /* [0,1] == [No,Yes] */
u8 ErrorRecoveryLevel; /* [0..2] */ u8 ErrorRecoveryLevel; /* [0..2] */
u8 SessionType; /* [0,1] == [Normal,Discovery]*/ u8 SessionType; /* [0,1] == [Normal,Discovery]*/
/*
* iSER specific session parameters
*/
u8 RDMAExtensions; /* [0,1] == [No,Yes] */
}; };
struct iscsi_queue_req { struct iscsi_queue_req {
@ -284,6 +293,7 @@ struct iscsi_data_count {
}; };
struct iscsi_param_list { struct iscsi_param_list {
bool iser;
struct list_head param_list; struct list_head param_list;
struct list_head extra_response_list; struct list_head extra_response_list;
}; };
@ -475,6 +485,7 @@ struct iscsi_cmd {
u32 first_data_sg_off; u32 first_data_sg_off;
u32 kmapped_nents; u32 kmapped_nents;
sense_reason_t sense_reason; sense_reason_t sense_reason;
void (*release_cmd)(struct iscsi_cmd *);
} ____cacheline_aligned; } ____cacheline_aligned;
struct iscsi_tmr_req { struct iscsi_tmr_req {
@ -503,6 +514,7 @@ struct iscsi_conn {
u16 login_port; u16 login_port;
u16 local_port; u16 local_port;
int net_size; int net_size;
int login_family;
u32 auth_id; u32 auth_id;
u32 conn_flags; u32 conn_flags;
/* Used for iscsi_tx_login_rsp() */ /* Used for iscsi_tx_login_rsp() */
@ -562,9 +574,12 @@ struct iscsi_conn {
struct list_head immed_queue_list; struct list_head immed_queue_list;
struct list_head response_queue_list; struct list_head response_queue_list;
struct iscsi_conn_ops *conn_ops; struct iscsi_conn_ops *conn_ops;
struct iscsi_login *conn_login;
struct iscsit_transport *conn_transport;
struct iscsi_param_list *param_list; struct iscsi_param_list *param_list;
/* Used for per connection auth state machine */ /* Used for per connection auth state machine */
void *auth_protocol; void *auth_protocol;
void *context;
struct iscsi_login_thread_s *login_thread; struct iscsi_login_thread_s *login_thread;
struct iscsi_portal_group *tpg; struct iscsi_portal_group *tpg;
/* Pointer to parent session */ /* Pointer to parent session */
@ -663,6 +678,8 @@ struct iscsi_login {
u8 first_request; u8 first_request;
u8 version_min; u8 version_min;
u8 version_max; u8 version_max;
u8 login_complete;
u8 login_failed;
char isid[6]; char isid[6];
u32 cmd_sn; u32 cmd_sn;
itt_t init_task_tag; itt_t init_task_tag;
@ -670,10 +687,11 @@ struct iscsi_login {
u32 rsp_length; u32 rsp_length;
u16 cid; u16 cid;
u16 tsih; u16 tsih;
char *req; char req[ISCSI_HDR_LEN];
char *rsp; char rsp[ISCSI_HDR_LEN];
char *req_buf; char *req_buf;
char *rsp_buf; char *rsp_buf;
struct iscsi_conn *conn;
} ____cacheline_aligned; } ____cacheline_aligned;
struct iscsi_node_attrib { struct iscsi_node_attrib {
@ -754,6 +772,8 @@ struct iscsi_np {
struct task_struct *np_thread; struct task_struct *np_thread;
struct timer_list np_login_timer; struct timer_list np_login_timer;
struct iscsi_portal_group *np_login_tpg; struct iscsi_portal_group *np_login_tpg;
void *np_context;
struct iscsit_transport *np_transport;
struct list_head np_list; struct list_head np_list;
} ____cacheline_aligned; } ____cacheline_aligned;

View file

@ -60,8 +60,13 @@ void iscsit_increment_maxcmdsn(struct iscsi_cmd *cmd, struct iscsi_session *sess
cmd->maxcmdsn_inc = 1; 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; sess->max_cmd_sn += 1;
pr_debug("Updated MaxCmdSN to 0x%08x\n", sess->max_cmd_sn); pr_debug("Updated MaxCmdSN to 0x%08x\n", sess->max_cmd_sn);
mutex_unlock(&sess->cmdsn_mutex); mutex_unlock(&sess->cmdsn_mutex);
} }
EXPORT_SYMBOL(iscsit_increment_maxcmdsn);

View file

@ -22,6 +22,7 @@
#include <scsi/iscsi_proto.h> #include <scsi/iscsi_proto.h>
#include <target/target_core_base.h> #include <target/target_core_base.h>
#include <target/target_core_fabric.h> #include <target/target_core_fabric.h>
#include <target/iscsi/iscsi_transport.h>
#include "iscsi_target_core.h" #include "iscsi_target_core.h"
#include "iscsi_target_seq_pdu_list.h" #include "iscsi_target_seq_pdu_list.h"
@ -53,6 +54,9 @@ int iscsit_dump_data_payload(
u32 length, padding, offset = 0, size; u32 length, padding, offset = 0, size;
struct kvec iov; struct kvec iov;
if (conn->sess->sess_ops->RDMAExtensions)
return 0;
length = (buf_len > OFFLOAD_BUF_SIZE) ? OFFLOAD_BUF_SIZE : buf_len; length = (buf_len > OFFLOAD_BUF_SIZE) ? OFFLOAD_BUF_SIZE : buf_len;
buf = kzalloc(length, GFP_ATOMIC); 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) int iscsit_execute_cmd(struct iscsi_cmd *cmd, int ooo)
{ {
struct se_cmd *se_cmd = &cmd->se_cmd; struct se_cmd *se_cmd = &cmd->se_cmd;
struct iscsi_conn *conn = cmd->conn;
int lr = 0; int lr = 0;
spin_lock_bh(&cmd->istate_lock); spin_lock_bh(&cmd->istate_lock);
@ -981,7 +986,7 @@ int iscsit_execute_cmd(struct iscsi_cmd *cmd, int ooo)
return 0; return 0;
iscsit_set_dataout_sequence_values(cmd); 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; 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) if (transport_check_aborted_status(se_cmd, 1) != 0)
return 0; return 0;
iscsit_set_dataout_sequence_values(cmd); iscsit_set_unsoliticed_dataout(cmd);
spin_lock_bh(&cmd->dataout_timeout_lock);
iscsit_start_dataout_timer(cmd, cmd->conn);
spin_unlock_bh(&cmd->dataout_timeout_lock);
} }
return transport_handle_cdb_direct(&cmd->se_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); cmd->init_task_tag);
spin_unlock_bh(&cmd->dataout_timeout_lock); spin_unlock_bh(&cmd->dataout_timeout_lock);
} }
EXPORT_SYMBOL(iscsit_stop_dataout_timer);

View file

@ -39,8 +39,39 @@
#include "iscsi_target.h" #include "iscsi_target.h"
#include "iscsi_target_parameters.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_waitqueue_head(&conn->queues_wq);
INIT_LIST_HEAD(&conn->conn_list); INIT_LIST_HEAD(&conn->conn_list);
INIT_LIST_HEAD(&conn->conn_cmd_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)) { if (!zalloc_cpumask_var(&conn->conn_cpumask, GFP_KERNEL)) {
pr_err("Unable to allocate conn->conn_cpumask\n"); 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_node_attrib *na;
struct iscsi_session *sess = conn->sess; struct iscsi_session *sess = conn->sess;
unsigned char buf[32]; unsigned char buf[32];
bool iser = false;
sess->tpg = conn->tpg; sess->tpg = conn->tpg;
@ -319,7 +362,10 @@ static int iscsi_login_zero_tsih_s2(
return -1; 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) if (sess->sess_ops->SessionType)
return iscsi_set_keys_irrelevant_for_discovery( 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) if (iscsi_login_disable_FIM_keys(conn->param_list, conn) < 0)
return -1; 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; 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_portal_group *se_tpg = &tpg->tpg_se_tpg;
struct se_session *se_sess, *se_sess_tmp; struct se_session *se_sess, *se_sess_tmp;
struct iscsi_login_req *pdu = (struct iscsi_login_req *)buf; struct iscsi_login_req *pdu = (struct iscsi_login_req *)buf;
bool iser = false;
spin_lock_bh(&se_tpg->session_lock); spin_lock_bh(&se_tpg->session_lock);
list_for_each_entry_safe(se_sess, se_sess_tmp, &se_tpg->tpg_sess_list, 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; 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 * Need to send TargetPortalGroupTag back in first login response
* on any iSCSI connection where the Initiator provides TargetName. * 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) static void iscsi_post_login_start_timers(struct iscsi_conn *conn)
{ {
struct iscsi_session *sess = conn->sess; 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) if (!sess->sess_ops->SessionType)
iscsit_start_nopin_timer(conn); iscsit_start_nopin_timer(conn);
@ -632,6 +737,7 @@ static int iscsi_post_login_handler(
spin_unlock_bh(&sess->conn_lock); spin_unlock_bh(&sess->conn_lock);
iscsi_post_login_start_timers(conn); iscsi_post_login_start_timers(conn);
iscsi_activate_thread_set(conn, ts); iscsi_activate_thread_set(conn, ts);
/* /*
* Determine CPU mask to ensure connection's RX and TX kthreads * 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); spin_unlock_bh(&np->np_thread_lock);
} }
int iscsi_target_setup_login_socket( int iscsit_setup_np(
struct iscsi_np *np, struct iscsi_np *np,
struct __kernel_sockaddr_storage *sockaddr) struct __kernel_sockaddr_storage *sockaddr)
{ {
struct socket *sock; struct socket *sock = NULL;
int backlog = 5, ret, opt = 0, len; int backlog = 5, ret, opt = 0, len;
switch (np->np_network_transport) { switch (np->np_network_transport) {
@ -781,15 +887,15 @@ int iscsi_target_setup_login_socket(
np->np_ip_proto = IPPROTO_SCTP; np->np_ip_proto = IPPROTO_SCTP;
np->np_sock_type = SOCK_SEQPACKET; np->np_sock_type = SOCK_SEQPACKET;
break; break;
case ISCSI_IWARP_TCP:
case ISCSI_IWARP_SCTP:
case ISCSI_INFINIBAND:
default: default:
pr_err("Unsupported network_transport: %d\n", pr_err("Unsupported network_transport: %d\n",
np->np_network_transport); np->np_network_transport);
return -EINVAL; return -EINVAL;
} }
np->np_ip_proto = IPPROTO_TCP;
np->np_sock_type = SOCK_STREAM;
ret = sock_create(sockaddr->ss_family, np->np_sock_type, ret = sock_create(sockaddr->ss_family, np->np_sock_type,
np->np_ip_proto, &sock); np->np_ip_proto, &sock);
if (ret < 0) { if (ret < 0) {
@ -853,7 +959,6 @@ int iscsi_target_setup_login_socket(
} }
return 0; return 0;
fail: fail:
np->np_socket = NULL; np->np_socket = NULL;
if (sock) if (sock)
@ -861,21 +966,169 @@ int iscsi_target_setup_login_socket(
return ret; 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) static int __iscsi_target_login_thread(struct iscsi_np *np)
{ {
u8 buffer[ISCSI_HDR_LEN], iscsi_opcode, zero_tsih = 0; u8 *buffer, zero_tsih = 0;
int err, ret = 0, stop; int ret = 0, rc, stop;
struct iscsi_conn *conn = NULL; struct iscsi_conn *conn = NULL;
struct iscsi_login *login; struct iscsi_login *login;
struct iscsi_portal_group *tpg = NULL; struct iscsi_portal_group *tpg = NULL;
struct socket *new_sock, *sock;
struct kvec iov;
struct iscsi_login_req *pdu; struct iscsi_login_req *pdu;
struct sockaddr_in sock_in;
struct sockaddr_in6 sock_in6;
flush_signals(current); flush_signals(current);
sock = np->np_socket;
spin_lock_bh(&np->np_thread_lock); spin_lock_bh(&np->np_thread_lock);
if (np->np_thread_state == ISCSI_NP_THREAD_RESET) { 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); 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); conn = kzalloc(sizeof(struct iscsi_conn), GFP_KERNEL);
if (!conn) { if (!conn) {
pr_err("Could not allocate memory for" pr_err("Could not allocate memory for"
" new connection\n"); " new connection\n");
sock_release(new_sock);
/* Get another socket */ /* Get another socket */
return 1; return 1;
} }
pr_debug("Moving to TARG_CONN_STATE_FREE.\n"); pr_debug("Moving to TARG_CONN_STATE_FREE.\n");
conn->conn_state = TARG_CONN_STATE_FREE; conn->conn_state = TARG_CONN_STATE_FREE;
conn->sock = new_sock;
pr_debug("Moving to TARG_CONN_STATE_XPT_UP.\n"); if (iscsit_conn_set_transport(conn, np->np_transport) < 0) {
conn->conn_state = TARG_CONN_STATE_XPT_UP; kfree(conn);
return 1;
}
/* rc = np->np_transport->iscsit_accept_np(np, conn);
* Allocate conn->conn_ops early as a failure calling if (rc == -ENOSYS) {
* iscsit_tx_login_rsp() below will call tx_data(). complete(&np->np_restart_comp);
*/ iscsit_put_transport(conn->conn_transport);
conn->conn_ops = kzalloc(sizeof(struct iscsi_conn_ops), GFP_KERNEL); kfree(conn);
if (!conn->conn_ops) { conn = NULL;
pr_err("Unable to allocate memory for" goto exit;
" struct iscsi_conn_ops.\n"); } else if (rc < 0) {
goto new_sess_out; 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.. * Perform the remaining iSCSI connection initialization items..
*/ */
if (iscsi_login_init_conn(conn) < 0) login = iscsi_login_init_conn(conn);
goto new_sess_out; if (!login) {
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");
goto new_sess_out; goto new_sess_out;
} }
iscsi_opcode = (buffer[0] & ISCSI_OPCODE_MASK); iscsi_start_login_thread_timer(np);
if (!(iscsi_opcode & ISCSI_OP_LOGIN)) {
pr_err("First opcode is not login request," pr_debug("Moving to TARG_CONN_STATE_XPT_UP.\n");
" failing login request.\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; 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 * Used by iscsit_tx_login_rsp() for Login Resonses PDUs
* when Status-Class != 0. * when Status-Class != 0.
*/ */
conn->login_itt = pdu->itt; conn->login_itt = pdu->itt;
spin_lock_bh(&np->np_thread_lock); spin_lock_bh(&np->np_thread_lock);
if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) { 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); 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; conn->network_transport = np->np_network_transport;
pr_debug("Received iSCSI login request from %s on %s Network" pr_debug("Received iSCSI login request from %s on %s Network"
" Portal %s:%hu\n", conn->login_ip, " Portal %s:%hu\n", conn->login_ip, np->np_transport->name,
(conn->network_transport == ISCSI_TCP) ? "TCP" : "SCTP", conn->local_ip, conn->local_port);
conn->local_ip, conn->local_port);
pr_debug("Moving to TARG_CONN_STATE_IN_LOGIN.\n"); pr_debug("Moving to TARG_CONN_STATE_IN_LOGIN.\n");
conn->conn_state = TARG_CONN_STATE_IN_LOGIN; 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) if (iscsi_login_non_zero_tsih_s1(conn, buffer) < 0)
goto new_sess_out; goto new_sess_out;
} }
/* /*
* This will process the first login request, and call * SessionType: Discovery
* iscsi_target_locate_portal(), and return a valid struct iscsi_login. *
* Locates Default Portal
*
* SessionType: Normal
*
* Locates Target Portal from NP -> Target IQN
*/ */
login = iscsi_target_init_negotiation(np, conn, buffer); rc = iscsi_target_locate_portal(np, conn, login);
if (!login) { if (rc < 0) {
tpg = conn->tpg; tpg = conn->tpg;
goto new_sess_out; goto new_sess_out;
} }
@ -1068,15 +1276,11 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
} }
if (zero_tsih) { if (zero_tsih) {
if (iscsi_login_zero_tsih_s2(conn) < 0) { if (iscsi_login_zero_tsih_s2(conn) < 0)
iscsi_target_nego_release(login, conn);
goto new_sess_out; goto new_sess_out;
}
} else { } else {
if (iscsi_login_non_zero_tsih_s2(conn, buffer) < 0) { if (iscsi_login_non_zero_tsih_s2(conn, buffer) < 0)
iscsi_target_nego_release(login, conn);
goto old_sess_out; goto old_sess_out;
}
} }
if (iscsi_target_start_negotiation(login, conn) < 0) 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); iscsi_release_param_list(conn->param_list);
conn->param_list = NULL; conn->param_list = NULL;
} }
if (conn->sock) iscsi_target_nego_release(conn);
if (conn->sock) {
sock_release(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); kfree(conn);
if (tpg) { if (tpg) {
@ -1172,11 +1386,13 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
/* Wait for another socket.. */ /* Wait for another socket.. */
if (!stop) if (!stop)
return 1; return 1;
exit:
iscsi_stop_login_thread_timer(np); iscsi_stop_login_thread_timer(np);
spin_lock_bh(&np->np_thread_lock); spin_lock_bh(&np->np_thread_lock);
np->np_thread_state = ISCSI_NP_THREAD_EXIT; np->np_thread_state = ISCSI_NP_THREAD_EXIT;
np->np_thread = NULL;
spin_unlock_bh(&np->np_thread_lock); spin_unlock_bh(&np->np_thread_lock);
return 0; return 0;
} }

View file

@ -4,8 +4,14 @@
extern int iscsi_login_setup_crypto(struct iscsi_conn *); extern int iscsi_login_setup_crypto(struct iscsi_conn *);
extern int iscsi_check_for_session_reinstatement(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 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 *, extern int iscsi_target_setup_login_socket(struct iscsi_np *,
struct __kernel_sockaddr_storage *); 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_target_login_thread(void *);
extern int iscsi_login_disable_FIM_keys(struct iscsi_param_list *, struct iscsi_conn *); extern int iscsi_login_disable_FIM_keys(struct iscsi_param_list *, struct iscsi_conn *);

View file

@ -22,6 +22,7 @@
#include <scsi/iscsi_proto.h> #include <scsi/iscsi_proto.h>
#include <target/target_core_base.h> #include <target/target_core_base.h>
#include <target/target_core_fabric.h> #include <target/target_core_fabric.h>
#include <target/iscsi/iscsi_transport.h>
#include "iscsi_target_core.h" #include "iscsi_target_core.h"
#include "iscsi_target_parameters.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); kfree(conn->auth_protocol);
} }
static int iscsi_target_check_login_request( int iscsi_target_check_login_request(
struct iscsi_conn *conn, struct iscsi_conn *conn,
struct iscsi_login *login) struct iscsi_login *login)
{ {
@ -200,8 +201,8 @@ static int iscsi_target_check_login_request(
return -1; return -1;
} }
req_csg = (login_req->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2; req_csg = ISCSI_LOGIN_CURRENT_STAGE(login_req->flags);
req_nsg = (login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK); req_nsg = ISCSI_LOGIN_NEXT_STAGE(login_req->flags);
if (req_csg != login->current_stage) { if (req_csg != login->current_stage) {
pr_err("Initiator unexpectedly changed login 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); padding = ((-login->rsp_length) & 3);
if (iscsi_login_tx_data( if (conn->conn_transport->iscsit_put_login_tx(conn, login,
conn, login->rsp_length + padding) < 0)
login->rsp,
login->rsp_buf,
login->rsp_length + padding) < 0)
return -1; return -1;
login->rsp_length = 0; 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; 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) 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) if (iscsi_target_do_tx_login_io(conn, login) < 0)
return -1; return -1;
if (iscsi_target_do_rx_login_io(conn, login) < 0) if (conn->conn_transport->iscsit_get_login_rx(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)
return -1; return -1;
return 0; return 0;
@ -681,9 +619,9 @@ static int iscsi_target_do_login(struct iscsi_conn *conn, struct iscsi_login *lo
return -1; return -1;
} }
switch ((login_req->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2) { switch (ISCSI_LOGIN_CURRENT_STAGE(login_req->flags)) {
case 0: 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) if (iscsi_target_handle_csg_zero(conn, login) < 0)
return -1; return -1;
break; break;
@ -693,6 +631,7 @@ static int iscsi_target_do_login(struct iscsi_conn *conn, struct iscsi_login *lo
return -1; return -1;
if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) { if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) {
login->tsih = conn->sess->tsih; login->tsih = conn->sess->tsih;
login->login_complete = 1;
if (iscsi_target_do_tx_login_io(conn, if (iscsi_target_do_tx_login_io(conn,
login) < 0) login) < 0)
return -1; return -1;
@ -702,8 +641,7 @@ static int iscsi_target_do_login(struct iscsi_conn *conn, struct iscsi_login *lo
default: default:
pr_err("Illegal CSG: %d received from" pr_err("Illegal CSG: %d received from"
" Initiator, protocol error.\n", " Initiator, protocol error.\n",
(login_req->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) ISCSI_LOGIN_CURRENT_STAGE(login_req->flags));
>> 2);
break; break;
} }
@ -737,7 +675,7 @@ static void iscsi_initiatorname_tolower(
/* /*
* Processes the first Login Request.. * Processes the first Login Request..
*/ */
static int iscsi_target_locate_portal( int iscsi_target_locate_portal(
struct iscsi_np *np, struct iscsi_np *np,
struct iscsi_conn *conn, struct iscsi_conn *conn,
struct iscsi_login *login) struct iscsi_login *login)
@ -753,22 +691,6 @@ static int iscsi_target_locate_portal(
login_req = (struct iscsi_login_req *) login->req; login_req = (struct iscsi_login_req *) login->req;
payload_length = ntoh24(login_req->dlength); 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); tmpbuf = kzalloc(payload_length + 1, GFP_KERNEL);
if (!tmpbuf) { if (!tmpbuf) {
pr_err("Unable to allocate memory for tmpbuf.\n"); 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; 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. * See 5.3. Login Phase.
*/ */
@ -958,100 +882,30 @@ static int iscsi_target_locate_portal(
return ret; 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( int iscsi_target_start_negotiation(
struct iscsi_login *login, struct iscsi_login *login,
struct iscsi_conn *conn) struct iscsi_conn *conn)
{ {
int ret = -1; int ret;
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;
}
ret = iscsi_target_do_login(conn, login); ret = iscsi_target_do_login(conn, login);
out:
if (ret != 0) if (ret != 0)
iscsi_remove_failed_auth_entry(conn); iscsi_remove_failed_auth_entry(conn);
iscsi_target_nego_release(login, conn); iscsi_target_nego_release(conn);
return ret; return ret;
} }
void iscsi_target_nego_release( void iscsi_target_nego_release(struct iscsi_conn *conn)
struct iscsi_login *login,
struct iscsi_conn *conn)
{ {
kfree(login->req); struct iscsi_login *login = conn->conn_login;
kfree(login->rsp);
if (!login)
return;
kfree(login->req_buf); kfree(login->req_buf);
kfree(login->rsp_buf); kfree(login->rsp_buf);
kfree(login); kfree(login);
conn->conn_login = NULL;
} }

View file

@ -7,11 +7,14 @@
extern void convert_null_to_semi(char *, int); extern void convert_null_to_semi(char *, int);
extern int extract_param(const char *, const char *, unsigned int, char *, extern int extract_param(const char *, const char *, unsigned int, char *,
unsigned char *); unsigned char *);
extern struct iscsi_login *iscsi_target_init_negotiation( extern int iscsi_target_check_login_request(struct iscsi_conn *,
struct iscsi_np *, struct iscsi_conn *, char *); 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( extern int iscsi_target_start_negotiation(
struct iscsi_login *, struct iscsi_conn *); struct iscsi_login *, struct iscsi_conn *);
extern void iscsi_target_nego_release( extern void iscsi_target_nego_release(struct iscsi_conn *);
struct iscsi_login *, struct iscsi_conn *);
#endif /* ISCSI_TARGET_NEGO_H */ #endif /* ISCSI_TARGET_NEGO_H */

View file

@ -59,7 +59,7 @@ int iscsi_login_tx_data(
char *text_buf, char *text_buf,
int text_length) int text_length)
{ {
int length, tx_sent; int length, tx_sent, iov_cnt = 1;
struct kvec iov[2]; struct kvec iov[2];
length = (ISCSI_HDR_LEN + text_length); length = (ISCSI_HDR_LEN + text_length);
@ -67,8 +67,12 @@ int iscsi_login_tx_data(
memset(&iov[0], 0, 2 * sizeof(struct kvec)); memset(&iov[0], 0, 2 * sizeof(struct kvec));
iov[0].iov_len = ISCSI_HDR_LEN; iov[0].iov_len = ISCSI_HDR_LEN;
iov[0].iov_base = pdu_buf; 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. * Initial Marker-less Interval.
@ -77,7 +81,7 @@ int iscsi_login_tx_data(
*/ */
conn->if_marker += length; 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) { if (tx_sent != length) {
pr_err("tx_data returned %d, expecting %d.\n", pr_err("tx_data returned %d, expecting %d.\n",
tx_sent, length); tx_sent, length);
@ -429,6 +433,28 @@ int iscsi_create_default_params(struct iscsi_param_list **param_list_ptr)
TYPERANGE_MARKINT, USE_INITIAL_ONLY); TYPERANGE_MARKINT, USE_INITIAL_ONLY);
if (!param) if (!param)
goto out; 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; *param_list_ptr = pl;
return 0; 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 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; struct iscsi_param *param;
param_list->iser = iser;
list_for_each_entry(param, &param_list->param_list, p_list) { list_for_each_entry(param, &param_list->param_list, p_list) {
param->state = 0; param->state = 0;
if (!strcmp(param->name, AUTHMETHOD)) { if (!strcmp(param->name, AUTHMETHOD)) {
SET_PSTATE_NEGOTIATE(param); SET_PSTATE_NEGOTIATE(param);
} else if (!strcmp(param->name, HEADERDIGEST)) { } else if (!strcmp(param->name, HEADERDIGEST)) {
SET_PSTATE_NEGOTIATE(param); if (iser == false)
SET_PSTATE_NEGOTIATE(param);
} else if (!strcmp(param->name, DATADIGEST)) { } else if (!strcmp(param->name, DATADIGEST)) {
SET_PSTATE_NEGOTIATE(param); if (iser == false)
SET_PSTATE_NEGOTIATE(param);
} else if (!strcmp(param->name, MAXCONNECTIONS)) { } else if (!strcmp(param->name, MAXCONNECTIONS)) {
SET_PSTATE_NEGOTIATE(param); SET_PSTATE_NEGOTIATE(param);
} else if (!strcmp(param->name, TARGETNAME)) { } else if (!strcmp(param->name, TARGETNAME)) {
@ -469,7 +499,8 @@ int iscsi_set_keys_to_negotiate(
} else if (!strcmp(param->name, IMMEDIATEDATA)) { } else if (!strcmp(param->name, IMMEDIATEDATA)) {
SET_PSTATE_NEGOTIATE(param); SET_PSTATE_NEGOTIATE(param);
} else if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) { } else if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) {
SET_PSTATE_NEGOTIATE(param); if (iser == false)
SET_PSTATE_NEGOTIATE(param);
} else if (!strcmp(param->name, MAXXMITDATASEGMENTLENGTH)) { } else if (!strcmp(param->name, MAXXMITDATASEGMENTLENGTH)) {
continue; continue;
} else if (!strcmp(param->name, MAXBURSTLENGTH)) { } else if (!strcmp(param->name, MAXBURSTLENGTH)) {
@ -498,6 +529,15 @@ int iscsi_set_keys_to_negotiate(
SET_PSTATE_NEGOTIATE(param); SET_PSTATE_NEGOTIATE(param);
} else if (!strcmp(param->name, OFMARKINT)) { } else if (!strcmp(param->name, OFMARKINT)) {
SET_PSTATE_NEGOTIATE(param); 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; param->state &= ~PSTATE_NEGOTIATE;
else if (!strcmp(param->name, OFMARKINT)) else if (!strcmp(param->name, OFMARKINT))
param->state &= ~PSTATE_NEGOTIATE; 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; return 0;
@ -1755,6 +1801,9 @@ void iscsi_set_connection_parameters(
* this key is not sent over the wire. * this key is not sent over the wire.
*/ */
if (!strcmp(param->name, MAXXMITDATASEGMENTLENGTH)) { if (!strcmp(param->name, MAXXMITDATASEGMENTLENGTH)) {
if (param_list->iser == true)
continue;
ops->MaxXmitDataSegmentLength = ops->MaxXmitDataSegmentLength =
simple_strtoul(param->value, &tmpptr, 0); simple_strtoul(param->value, &tmpptr, 0);
pr_debug("MaxXmitDataSegmentLength: %s\n", pr_debug("MaxXmitDataSegmentLength: %s\n",
@ -1800,6 +1849,22 @@ void iscsi_set_connection_parameters(
simple_strtoul(param->value, &tmpptr, 0); simple_strtoul(param->value, &tmpptr, 0);
pr_debug("IFMarkInt: %s\n", pr_debug("IFMarkInt: %s\n",
param->value); 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("----------------------------------------------------" pr_debug("----------------------------------------------------"
@ -1912,6 +1977,10 @@ void iscsi_set_session_parameters(
ops->SessionType = !strcmp(param->value, DISCOVERY); ops->SessionType = !strcmp(param->value, DISCOVERY);
pr_debug("SessionType: %s\n", pr_debug("SessionType: %s\n",
param->value); param->value);
} else if (!strcmp(param->name, RDMAEXTENTIONS)) {
ops->RDMAExtensions = !strcmp(param->value, YES);
pr_debug("RDMAExtensions: %s\n",
param->value);
} }
} }
pr_debug("----------------------------------------------------" pr_debug("----------------------------------------------------"

View file

@ -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_dump_sess_ops(struct iscsi_sess_ops *);
extern void iscsi_print_params(struct iscsi_param_list *); extern void iscsi_print_params(struct iscsi_param_list *);
extern int iscsi_create_default_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_set_keys_irrelevant_for_discovery(struct iscsi_param_list *);
extern int iscsi_copy_param_list(struct iscsi_param_list **, extern int iscsi_copy_param_list(struct iscsi_param_list **,
struct iscsi_param_list *, int); 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_NEW "X-com.cisco.protocol"
#define X_EXTENSIONKEY_CISCO_OLD "X-com.cisco.iscsi.draft" #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. * For AuthMethod.
*/ */
@ -132,6 +139,13 @@ extern void iscsi_set_session_parameters(struct iscsi_sess_ops *,
#define INITIAL_IFMARKINT "2048~65535" #define INITIAL_IFMARKINT "2048~65535"
#define INITIAL_OFMARKINT "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. * For [Header,Data]Digests.
*/ */

View file

@ -23,6 +23,7 @@
#include <scsi/iscsi_proto.h> #include <scsi/iscsi_proto.h>
#include <target/target_core_base.h> #include <target/target_core_base.h>
#include <target/target_core_fabric.h> #include <target/target_core_fabric.h>
#include <target/iscsi/iscsi_transport.h>
#include "iscsi_target_core.h" #include "iscsi_target_core.h"
#include "iscsi_target_seq_pdu_list.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. * 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( 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; return 0;
} }
EXPORT_SYMBOL(iscsit_tmr_post_handler);
/* /*
* Nothing to do here, but leave it for good measure. :-) * Nothing to do here, but leave it for good measure. :-)

View file

@ -31,6 +31,8 @@
#include "iscsi_target.h" #include "iscsi_target.h"
#include "iscsi_target_parameters.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 *iscsit_alloc_portal_group(struct iscsi_tiqn *tiqn, u16 tpgt)
{ {
struct iscsi_portal_group *tpg; 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", 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, 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; 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", 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, 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_np = NULL;
tpg_np->tpg = NULL; tpg_np->tpg = NULL;

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

View file

@ -24,6 +24,7 @@
#include <target/target_core_base.h> #include <target/target_core_base.h>
#include <target/target_core_fabric.h> #include <target/target_core_fabric.h>
#include <target/target_core_configfs.h> #include <target/target_core_configfs.h>
#include <target/iscsi/iscsi_transport.h>
#include "iscsi_target_core.h" #include "iscsi_target_core.h"
#include "iscsi_target_parameters.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); 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 * May be called from software interrupt (timer) context for allocating
* iSCSI NopINs. * iSCSI NopINs.
@ -156,13 +169,12 @@ struct iscsi_cmd *iscsit_allocate_cmd(struct iscsi_conn *conn, gfp_t gfp_mask)
{ {
struct iscsi_cmd *cmd; 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) { if (!cmd) {
pr_err("Unable to allocate memory for struct iscsi_cmd.\n"); pr_err("Unable to allocate memory for struct iscsi_cmd.\n");
return NULL; return NULL;
} }
cmd->conn = conn;
cmd->conn = conn;
INIT_LIST_HEAD(&cmd->i_conn_node); INIT_LIST_HEAD(&cmd->i_conn_node);
INIT_LIST_HEAD(&cmd->datain_list); INIT_LIST_HEAD(&cmd->datain_list);
INIT_LIST_HEAD(&cmd->cmd_r2t_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; return cmd;
} }
EXPORT_SYMBOL(iscsit_allocate_cmd);
struct iscsi_seq *iscsit_get_seq_holder_for_datain( struct iscsi_seq *iscsit_get_seq_holder_for_datain(
struct iscsi_cmd *cmd, struct iscsi_cmd *cmd,
@ -304,6 +317,7 @@ int iscsit_sequence_cmd(
return ret; return ret;
} }
EXPORT_SYMBOL(iscsit_sequence_cmd);
int iscsit_check_unsolicited_dataout(struct iscsi_cmd *cmd, unsigned char *buf) 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) { switch (cmd->iscsi_opcode) {
case ISCSI_OP_SCSI_CMD: case ISCSI_OP_SCSI_CMD:
if (cmd->data_direction == DMA_TO_DEVICE)
iscsit_stop_dataout_timer(cmd);
/*
* Fallthrough
*/
case ISCSI_OP_SCSI_TMFUNC: case ISCSI_OP_SCSI_TMFUNC:
transport_generic_free_cmd(&cmd->se_cmd, 1); transport_generic_free_cmd(&cmd->se_cmd, 1);
break; break;
@ -704,7 +723,7 @@ void iscsit_free_cmd(struct iscsi_cmd *cmd)
} }
/* Fall-through */ /* Fall-through */
default: default:
iscsit_release_cmd(cmd); cmd->release_cmd(cmd);
break; 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) 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_rsp *hdr;
struct iscsi_login *login = conn->conn_login;
login->login_failed = 1;
iscsit_collect_login_stats(conn, status_class, status_detail); iscsit_collect_login_stats(conn, status_class, status_detail);
memset(&iov, 0, sizeof(struct kvec)); hdr = (struct iscsi_login_rsp *)&login->rsp[0];
memset(&iscsi_hdr, 0x0, ISCSI_HDR_LEN);
hdr = (struct iscsi_login_rsp *)&iscsi_hdr;
hdr->opcode = ISCSI_OP_LOGIN_RSP; hdr->opcode = ISCSI_OP_LOGIN_RSP;
hdr->status_class = status_class; hdr->status_class = status_class;
hdr->status_detail = status_detail; hdr->status_detail = status_detail;
hdr->itt = conn->login_itt; hdr->itt = conn->login_itt;
iov.iov_base = &iscsi_hdr; return conn->conn_transport->iscsit_put_login_tx(conn, login, 0);
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;
} }
void iscsit_print_session_params(struct iscsi_session *sess) void iscsit_print_session_params(struct iscsi_session *sess)
@ -1432,7 +1436,8 @@ void iscsit_collect_login_stats(
strcpy(ls->last_intr_fail_name, strcpy(ls->last_intr_fail_name,
(intrname ? intrname->value : "Unknown")); (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, snprintf(ls->last_intr_fail_ip_addr, IPV6_ADDRESS_SPACE,
"%s", conn->login_ip); "%s", conn->login_ip);
ls->last_fail_time = get_jiffies_64(); ls->last_fail_time = get_jiffies_64();

View file

@ -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 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_r2t(struct iscsi_r2t *, struct iscsi_cmd *);
extern void iscsit_free_r2ts_from_list(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_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_datain(struct iscsi_cmd *, u32);
extern struct iscsi_seq *iscsit_get_seq_holder_for_r2t(struct iscsi_cmd *); extern struct iscsi_seq *iscsit_get_seq_holder_for_r2t(struct iscsi_cmd *);

View file

@ -30,8 +30,10 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/falloc.h>
#include <scsi/scsi.h> #include <scsi/scsi.h>
#include <scsi/scsi_host.h> #include <scsi/scsi_host.h>
#include <asm/unaligned.h>
#include <target/target_core_base.h> #include <target/target_core_base.h>
#include <target/target_core_backend.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", " block_device blocks: %llu logical_block_size: %d\n",
dev_size, div_u64(dev_size, fd_dev->fd_block_size), dev_size, div_u64(dev_size, fd_dev->fd_block_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 { } else {
if (!(fd_dev->fbd_flags & FBDF_HAS_SIZE)) { if (!(fd_dev->fbd_flags & FBDF_HAS_SIZE)) {
pr_err("FILEIO: Missing fd_dev_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_block_size = FD_BLOCKSIZE;
dev->dev_attrib.hw_max_sectors = FD_MAX_SECTORS; 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; 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_dev_id = fd_host->fd_host_dev_id_count++;
fd_dev->fd_queue_depth = dev->queue_depth; 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," 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, " %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; 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 static sense_reason_t
fd_execute_rw(struct se_cmd *cmd) fd_execute_rw(struct se_cmd *cmd)
{ {
@ -600,6 +710,8 @@ static struct sbc_ops fd_sbc_ops = {
.execute_rw = fd_execute_rw, .execute_rw = fd_execute_rw,
.execute_sync_cache = fd_execute_sync_cache, .execute_sync_cache = fd_execute_sync_cache,
.execute_write_same = fd_execute_write_same, .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 static sense_reason_t

View file

@ -379,105 +379,41 @@ iblock_execute_sync_cache(struct se_cmd *cmd)
return 0; 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 static sense_reason_t
iblock_execute_unmap(struct se_cmd *cmd) iblock_execute_unmap(struct se_cmd *cmd)
{ {
struct se_device *dev = cmd->se_dev; struct block_device *bdev = IBLOCK_DEV(cmd->se_dev)->ibd_bd;
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;
/* We never set ANC_SUP */ return sbc_execute_unmap(cmd, iblock_do_unmap, bdev);
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;
} }
static sense_reason_t static sense_reason_t
iblock_execute_write_same_unmap(struct se_cmd *cmd) iblock_execute_write_same_unmap(struct se_cmd *cmd)
{ {
struct iblock_dev *ib_dev = IBLOCK_DEV(cmd->se_dev); struct block_device *bdev = IBLOCK_DEV(cmd->se_dev)->ibd_bd;
int rc; 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, ret = iblock_do_unmap(cmd, bdev, lba, nolb);
sbc_get_write_same_sectors(cmd), GFP_KERNEL, 0); if (ret)
if (rc < 0) { return ret;
pr_warn("blkdev_issue_discard() failed: %d\n", rc);
return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
}
target_complete_cmd(cmd, GOOD); target_complete_cmd(cmd, GOOD);
return 0; return 0;

View file

@ -596,3 +596,88 @@ u32 sbc_get_device_type(struct se_device *dev)
return TYPE_DISK; return TYPE_DISK;
} }
EXPORT_SYMBOL(sbc_get_device_type); 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);

View file

@ -65,7 +65,6 @@ static void transport_complete_task_attr(struct se_cmd *cmd);
static void transport_handle_queue_full(struct se_cmd *cmd, static void transport_handle_queue_full(struct se_cmd *cmd,
struct se_device *dev); struct se_device *dev);
static int transport_generic_get_mem(struct se_cmd *cmd); 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 transport_put_cmd(struct se_cmd *cmd);
static void target_complete_ok_work(struct work_struct *work); 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 * @se_cmd: command descriptor to add
* @ack_kref: Signal that fabric will perform an ack target_put_sess_cmd() * @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) bool ack_kref)
{ {
unsigned long flags; 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); spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
return ret; return ret;
} }
EXPORT_SYMBOL(target_get_sess_cmd);
static void target_release_cmd_kref(struct kref *kref) 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 */ /* CURRENT ERROR */
buffer[0] = 0x70; buffer[0] = 0x70;
buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10; 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 */ /* LOGICAL UNIT COMMUNICATION FAILURE */
buffer[SPC_ASC_KEY_OFFSET] = 0x08; buffer[SPC_ASC_KEY_OFFSET] = 0x08;
break; break;

View file

@ -103,6 +103,13 @@ int ft_queue_data_in(struct se_cmd *se_cmd)
use_sg = !(remaining % 4); use_sg = !(remaining % 4);
while (remaining) { 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) { if (!mem_len) {
sg = sg_next(sg); sg = sg_next(sg);
mem_len = min((size_t)sg->length, remaining); 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; f_ctl |= FC_FC_END_SEQ;
fc_fill_fc_hdr(fp, FC_RCTL_DD_SOL_DATA, ep->did, ep->sid, fc_fill_fc_hdr(fp, FC_RCTL_DD_SOL_DATA, ep->did, ep->sid,
FC_TYPE_FCP, f_ctl, fh_off); 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) { if (error) {
/* XXX For now, initiator will retry */ /* XXX For now, initiator will retry */
pr_err_ratelimited("%s: Failed to send frame %p, " pr_err_ratelimited("%s: Failed to send frame %p, "

View file

@ -428,19 +428,12 @@ static int ft_prli(struct fc_rport_priv *rdata, u32 spp_len,
return ret; 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) static void ft_sess_free(struct kref *kref)
{ {
struct ft_sess *sess = container_of(kref, struct ft_sess, kref); struct ft_sess *sess = container_of(kref, struct ft_sess, kref);
transport_deregister_session(sess->se_sess); 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) void ft_sess_put(struct ft_sess *sess)

View file

@ -66,11 +66,13 @@ enum {
* TODO: debug and remove the workaround. * TODO: debug and remove the workaround.
*/ */
enum { 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_TARGET 256
#define VHOST_SCSI_MAX_VQ 128 #define VHOST_SCSI_MAX_VQ 128
#define VHOST_SCSI_MAX_EVENT 128
struct vhost_scsi { struct vhost_scsi {
/* Protected by vhost_scsi->dev.mutex */ /* Protected by vhost_scsi->dev.mutex */
@ -82,6 +84,12 @@ struct vhost_scsi {
struct vhost_work vs_completion_work; /* cmd completion work item */ struct vhost_work vs_completion_work; /* cmd completion work item */
struct llist_head vs_completion_list; /* cmd completion queue */ 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 */ /* 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; 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) static void vhost_scsi_free_cmd(struct tcm_vhost_cmd *tv_cmd)
{ {
struct se_cmd *se_cmd = &tv_cmd->tvc_se_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); 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 /* 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 * 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__); 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) 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) 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++) for (i = 0; i < VHOST_SCSI_MAX_VQ; i++)
vhost_scsi_flush_vq(vs, i); vhost_scsi_flush_vq(vs, i);
vhost_work_flush(&vs->dev, &vs->vs_completion_work); 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 * Called from vhost_scsi_ioctl() context to walk the list of available
* tcm_vhost_tpg with an active struct tcm_vhost_nexus * 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( static int vhost_scsi_set_endpoint(
struct vhost_scsi *vs, struct vhost_scsi *vs,
@ -820,26 +969,27 @@ static int vhost_scsi_set_endpoint(
int index, ret, i, len; int index, ret, i, len;
bool match = false; bool match = false;
mutex_lock(&tcm_vhost_mutex);
mutex_lock(&vs->dev.mutex); mutex_lock(&vs->dev.mutex);
/* Verify that ring has been setup correctly. */ /* Verify that ring has been setup correctly. */
for (index = 0; index < vs->dev.nvqs; ++index) { for (index = 0; index < vs->dev.nvqs; ++index) {
/* Verify that ring has been setup correctly. */ /* Verify that ring has been setup correctly. */
if (!vhost_vq_access_ok(&vs->vqs[index])) { if (!vhost_vq_access_ok(&vs->vqs[index])) {
mutex_unlock(&vs->dev.mutex); ret = -EFAULT;
return -EFAULT; goto out;
} }
} }
len = sizeof(vs_tpg[0]) * VHOST_SCSI_MAX_TARGET; len = sizeof(vs_tpg[0]) * VHOST_SCSI_MAX_TARGET;
vs_tpg = kzalloc(len, GFP_KERNEL); vs_tpg = kzalloc(len, GFP_KERNEL);
if (!vs_tpg) { if (!vs_tpg) {
mutex_unlock(&vs->dev.mutex); ret = -ENOMEM;
return -ENOMEM; goto out;
} }
if (vs->vs_tpg) if (vs->vs_tpg)
memcpy(vs_tpg, vs->vs_tpg, len); memcpy(vs_tpg, vs->vs_tpg, len);
mutex_lock(&tcm_vhost_mutex);
list_for_each_entry(tv_tpg, &tcm_vhost_list, tv_tpg_list) { list_for_each_entry(tv_tpg, &tcm_vhost_list, tv_tpg_list) {
mutex_lock(&tv_tpg->tv_tpg_mutex); mutex_lock(&tv_tpg->tv_tpg_mutex);
if (!tv_tpg->tpg_nexus) { 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 (!strcmp(tv_tport->tport_name, t->vhost_wwpn)) {
if (vs->vs_tpg && vs->vs_tpg[tv_tpg->tport_tpgt]) { 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); kfree(vs_tpg);
return -EEXIST; mutex_unlock(&tv_tpg->tv_tpg_mutex);
ret = -EEXIST;
goto out;
} }
tv_tpg->tv_tpg_vhost_count++; tv_tpg->tv_tpg_vhost_count++;
tv_tpg->vhost_scsi = vs;
vs_tpg[tv_tpg->tport_tpgt] = tv_tpg; vs_tpg[tv_tpg->tport_tpgt] = tv_tpg;
smp_mb__after_atomic_inc(); smp_mb__after_atomic_inc();
match = true; match = true;
} }
mutex_unlock(&tv_tpg->tv_tpg_mutex); mutex_unlock(&tv_tpg->tv_tpg_mutex);
} }
mutex_unlock(&tcm_vhost_mutex);
if (match) { if (match) {
memcpy(vs->vs_vhost_wwpn, t->vhost_wwpn, memcpy(vs->vs_vhost_wwpn, t->vhost_wwpn,
@ -893,7 +1042,9 @@ static int vhost_scsi_set_endpoint(
kfree(vs->vs_tpg); kfree(vs->vs_tpg);
vs->vs_tpg = vs_tpg; vs->vs_tpg = vs_tpg;
out:
mutex_unlock(&vs->dev.mutex); mutex_unlock(&vs->dev.mutex);
mutex_unlock(&tcm_vhost_mutex);
return ret; return ret;
} }
@ -908,6 +1059,7 @@ static int vhost_scsi_clear_endpoint(
int index, ret, i; int index, ret, i;
u8 target; u8 target;
mutex_lock(&tcm_vhost_mutex);
mutex_lock(&vs->dev.mutex); mutex_lock(&vs->dev.mutex);
/* Verify that ring has been setup correctly. */ /* Verify that ring has been setup correctly. */
for (index = 0; index < vs->dev.nvqs; ++index) { for (index = 0; index < vs->dev.nvqs; ++index) {
@ -918,8 +1070,8 @@ static int vhost_scsi_clear_endpoint(
} }
if (!vs->vs_tpg) { if (!vs->vs_tpg) {
mutex_unlock(&vs->dev.mutex); ret = 0;
return 0; goto err_dev;
} }
for (i = 0; i < VHOST_SCSI_MAX_TARGET; i++) { for (i = 0; i < VHOST_SCSI_MAX_TARGET; i++) {
@ -944,6 +1096,7 @@ static int vhost_scsi_clear_endpoint(
goto err_tpg; goto err_tpg;
} }
tv_tpg->tv_tpg_vhost_count--; tv_tpg->tv_tpg_vhost_count--;
tv_tpg->vhost_scsi = NULL;
vs->vs_tpg[target] = NULL; vs->vs_tpg[target] = NULL;
match = true; match = true;
mutex_unlock(&tv_tpg->tv_tpg_mutex); mutex_unlock(&tv_tpg->tv_tpg_mutex);
@ -964,14 +1117,16 @@ static int vhost_scsi_clear_endpoint(
vhost_scsi_flush(vs); vhost_scsi_flush(vs);
kfree(vs->vs_tpg); kfree(vs->vs_tpg);
vs->vs_tpg = NULL; vs->vs_tpg = NULL;
WARN_ON(vs->vs_events_nr);
mutex_unlock(&vs->dev.mutex); mutex_unlock(&vs->dev.mutex);
mutex_unlock(&tcm_vhost_mutex);
return 0; return 0;
err_tpg: err_tpg:
mutex_unlock(&tv_tpg->tv_tpg_mutex); mutex_unlock(&tv_tpg->tv_tpg_mutex);
err_dev: err_dev:
mutex_unlock(&vs->dev.mutex); mutex_unlock(&vs->dev.mutex);
mutex_unlock(&tcm_vhost_mutex);
return ret; return ret;
} }
@ -1003,6 +1158,10 @@ static int vhost_scsi_open(struct inode *inode, struct file *f)
return -ENOMEM; return -ENOMEM;
vhost_work_init(&s->vs_completion_work, vhost_scsi_complete_cmd_work); 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_CTL].handle_kick = vhost_scsi_ctl_handle_kick;
s->vqs[VHOST_SCSI_VQ_EVT].handle_kick = vhost_scsi_evt_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_scsi_clear_endpoint(s, &t);
vhost_dev_stop(&s->dev); vhost_dev_stop(&s->dev);
vhost_dev_cleanup(&s->dev, false); vhost_dev_cleanup(&s->dev, false);
/* Jobs can re-queue themselves in evt kick handler. Do extra flush. */
vhost_scsi_flush(s);
kfree(s); kfree(s);
return 0; return 0;
} }
@ -1040,8 +1201,11 @@ static long vhost_scsi_ioctl(struct file *f, unsigned int ioctl,
struct vhost_scsi_target backend; struct vhost_scsi_target backend;
void __user *argp = (void __user *)arg; void __user *argp = (void __user *)arg;
u64 __user *featurep = argp; u64 __user *featurep = argp;
u32 __user *eventsp = argp;
u32 events_missed;
u64 features; u64 features;
int r, abi_version = VHOST_SCSI_ABI_VERSION; int r, abi_version = VHOST_SCSI_ABI_VERSION;
struct vhost_virtqueue *vq = &vs->vqs[VHOST_SCSI_VQ_EVT];
switch (ioctl) { switch (ioctl) {
case VHOST_SCSI_SET_ENDPOINT: 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)) if (copy_to_user(argp, &abi_version, sizeof abi_version))
return -EFAULT; return -EFAULT;
return 0; 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: case VHOST_GET_FEATURES:
features = VHOST_SCSI_FEATURES; features = VHOST_SCSI_FEATURES;
if (copy_to_user(featurep, &features, sizeof 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"; 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, static int tcm_vhost_port_link(struct se_portal_group *se_tpg,
struct se_lun *lun) struct se_lun *lun)
{ {
struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg, struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg,
struct tcm_vhost_tpg, se_tpg); struct tcm_vhost_tpg, se_tpg);
mutex_lock(&tcm_vhost_mutex);
mutex_lock(&tv_tpg->tv_tpg_mutex); mutex_lock(&tv_tpg->tv_tpg_mutex);
tv_tpg->tv_tpg_port_count++; tv_tpg->tv_tpg_port_count++;
mutex_unlock(&tv_tpg->tv_tpg_mutex); mutex_unlock(&tv_tpg->tv_tpg_mutex);
tcm_vhost_hotplug(tv_tpg, lun);
mutex_unlock(&tcm_vhost_mutex);
return 0; return 0;
} }
static void tcm_vhost_port_unlink(struct se_portal_group *se_tpg, 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 *tv_tpg = container_of(se_tpg,
struct tcm_vhost_tpg, se_tpg); struct tcm_vhost_tpg, se_tpg);
mutex_lock(&tcm_vhost_mutex);
mutex_lock(&tv_tpg->tv_tpg_mutex); mutex_lock(&tv_tpg->tv_tpg_mutex);
tv_tpg->tv_tpg_port_count--; tv_tpg->tv_tpg_port_count--;
mutex_unlock(&tv_tpg->tv_tpg_mutex); 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( static struct se_node_acl *tcm_vhost_make_nodeacl(

View file

@ -53,6 +53,7 @@ struct tcm_vhost_nacl {
struct se_node_acl se_node_acl; struct se_node_acl se_node_acl;
}; };
struct vhost_scsi;
struct tcm_vhost_tpg { struct tcm_vhost_tpg {
/* Vhost port target portal group tag for TCM */ /* Vhost port target portal group tag for TCM */
u16 tport_tpgt; u16 tport_tpgt;
@ -70,6 +71,8 @@ struct tcm_vhost_tpg {
struct tcm_vhost_tport *tport; struct tcm_vhost_tport *tport;
/* Returned by tcm_vhost_make_tpg() */ /* Returned by tcm_vhost_make_tpg() */
struct se_portal_group se_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 { struct tcm_vhost_tport {
@ -83,6 +86,13 @@ struct tcm_vhost_tport {
struct se_wwn tport_wwn; 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 * As per request from MST, keep TCM_VHOST related ioctl defines out of
* linux/vhost.h (user-space) for now.. * 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) #define VHOST_SCSI_CLEAR_ENDPOINT _IOW(VHOST_VIRTIO, 0x41, struct vhost_scsi_target)
/* Changing this breaks userspace. */ /* Changing this breaks userspace. */
#define VHOST_SCSI_GET_ABI_VERSION _IOW(VHOST_VIRTIO, 0x42, int) #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)

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

View file

@ -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_rev(struct se_device *dev);
u32 sbc_get_device_type(struct se_device *dev); u32 sbc_get_device_type(struct se_device *dev);
sector_t sbc_get_write_same_sectors(struct se_cmd *cmd); 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 *); void transport_set_vpd_proto_id(struct t10_vpd *, unsigned char *);
int transport_set_vpd_assoc(struct t10_vpd *, unsigned char *); int transport_set_vpd_assoc(struct t10_vpd *, unsigned char *);

View file

@ -120,7 +120,7 @@ bool transport_wait_for_tasks(struct se_cmd *);
int transport_check_aborted_status(struct se_cmd *, int); int transport_check_aborted_status(struct se_cmd *, int);
int transport_send_check_condition_and_sense(struct se_cmd *, int transport_send_check_condition_and_sense(struct se_cmd *,
sense_reason_t, int); 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 *); int target_put_sess_cmd(struct se_session *, struct se_cmd *);
void target_sess_cmd_list_set_waiting(struct se_session *); void target_sess_cmd_list_set_waiting(struct se_session *);
void target_wait_for_sess_cmds(struct se_session *, int); void target_wait_for_sess_cmds(struct se_session *, int);