mirror of
https://github.com/SCST-project/scst.git
synced 2026-07-26 01:52:39 +00:00
Merge branch 'svn-trunk'
Conflicts: scst/README.dlm scst/include/scst.h scst/src/dev_handlers/scst_vdisk.c scst/src/scst_lib.c scst/src/scst_main.c scst/src/scst_pres.h scst/src/scst_sysfs.c scst/src/scst_targ.c www/downloads.html www/target_lsi.html
This commit is contained in:
@@ -45,7 +45,7 @@ QLA_DIR=qla2x00t_git/qla2x00-target
|
||||
QLA_OLD_INI_DIR=qla2x00t
|
||||
QLA_OLD_DIR=qla2x00t/qla2x00-target
|
||||
LSI_DIR=mpt
|
||||
USR_DIR=usr/fileio
|
||||
USR_DIR=usr
|
||||
SRP_DIR=srpt
|
||||
SCST_LOCAL_DIR=scst_local
|
||||
MVSAS_DIR=mvsas_tgt
|
||||
@@ -129,7 +129,7 @@ help:
|
||||
@echo " scst_local_install : scst_local target: install"
|
||||
@echo " scst_local_uninstall : scst_local target: uninstall"
|
||||
@echo ""
|
||||
@echo " usr : make user space fileio_tgt target"
|
||||
@echo " usr : make user space targets"
|
||||
@echo " usr_clean : usr target: clean "
|
||||
@echo " usr_extraclean : usr target: clean + clean dependencies"
|
||||
@echo " usr_install : usr target: install"
|
||||
|
||||
@@ -48,9 +48,15 @@ qla2x00t_git
|
||||
3. Symlink drivers/scsi/qla2xxx subdirectory in the cloned git tree to the
|
||||
qla2x00t_git subdirectory
|
||||
|
||||
Thats all. Now "make all" and other common and QLA specific root
|
||||
Makefile targets "magically" start working. For detail instructions how
|
||||
to setup QLA target build environment see its README or HOWTO.
|
||||
Thats all. Now run either "make all" to build the SCST core and all
|
||||
target drivers with in-tree QLogic drivers, or the following command
|
||||
with standalone QLogic drivers:
|
||||
|
||||
BUILD_2X_MODULE=y CONFIG_SCSI_QLA_FC=y CONFIG_SCSI_QLA2XXX_TARGET=y make all
|
||||
|
||||
and other common and QLA specific root Makefile targets "magically" start
|
||||
working. For detail instructions how to setup QLA target build environment see
|
||||
its README or HOWTO.
|
||||
|
||||
You can still build the old driver using qla_old* root Makefile targets.
|
||||
|
||||
|
||||
@@ -36,7 +36,9 @@ rtf: $(RTFS)
|
||||
$(COMMAND)rtf $(<)
|
||||
|
||||
clean:
|
||||
mv "Using the DLM as a Distributed In-Memory Database.pdf" "Using the DLM as a Distributed In-Memory Database.pdf_"
|
||||
rm -f *.txt *.html *.tex *.dvi *.ps *.pdf *.info *.lyx *.rtf
|
||||
mv "Using the DLM as a Distributed In-Memory Database.pdf_" "Using the DLM as a Distributed In-Memory Database.pdf"
|
||||
|
||||
extraclean: clean
|
||||
rm -f *.orig *.rej
|
||||
|
||||
Binary file not shown.
+1
-1
@@ -26,7 +26,7 @@
|
||||
#include "scst.h"
|
||||
#endif
|
||||
|
||||
#define FT_VERSION "3.1.0-pre"
|
||||
#define FT_VERSION "3.2.0-pre"
|
||||
#define FT_MODULE "fcst"
|
||||
|
||||
#define FT_MAX_HW_PENDING_TIME 20 /* max I/O time in seconds */
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
iSCSI SCST target driver
|
||||
========================
|
||||
|
||||
Version 3.1.0, XX XXXXX 2014
|
||||
Version 3.2.0, XX XXXXX 2014
|
||||
----------------------------
|
||||
|
||||
ISCSI-SCST is a deeply reworked fork of iSCSI Enterprise Target (IET)
|
||||
|
||||
@@ -21,4 +21,4 @@
|
||||
#define ISCSI_VERSION_STRING_SUFFIX
|
||||
#endif
|
||||
|
||||
#define ISCSI_VERSION_STRING "3.1.0-pre1" ISCSI_VERSION_STRING_SUFFIX
|
||||
#define ISCSI_VERSION_STRING "3.2.0-pre1" ISCSI_VERSION_STRING_SUFFIX
|
||||
|
||||
@@ -1297,6 +1297,7 @@ unsigned long iscsi_get_flow_ctrl_or_mgmt_dbg_log_flag(struct iscsi_cmnd *cmnd)
|
||||
flag = TRACE_MGMT_DEBUG;
|
||||
else {
|
||||
int status = scst_cmd_get_status(cmnd->scst_cmd);
|
||||
|
||||
if ((status == SAM_STAT_TASK_SET_FULL) ||
|
||||
(status == SAM_STAT_BUSY))
|
||||
flag = TRACE_FLOW_CONTROL;
|
||||
|
||||
@@ -525,6 +525,7 @@ static void conn_rsp_timer_fn(unsigned long arg)
|
||||
|
||||
if (!list_empty(&conn->write_timeout_list)) {
|
||||
unsigned long timeout_time;
|
||||
|
||||
cmnd = list_first_entry(&conn->write_timeout_list,
|
||||
struct iscsi_cmnd, write_timeout_list_entry);
|
||||
|
||||
@@ -583,7 +584,7 @@ static void conn_nop_in_delayed_work_fn(struct work_struct *work)
|
||||
#endif
|
||||
{
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
|
||||
struct iscsi_conn *conn = (struct iscsi_conn *)p;
|
||||
struct iscsi_conn *conn = p;
|
||||
#else
|
||||
struct iscsi_conn *conn = container_of(work, struct iscsi_conn,
|
||||
nop_in_delayed_work.work);
|
||||
@@ -834,6 +835,7 @@ void conn_free(struct iscsi_conn *conn)
|
||||
/* Just in case if new conn gets freed before the old one */
|
||||
if (test_bit(ISCSI_CONN_REINSTATING, &conn->conn_aflags)) {
|
||||
struct iscsi_conn *c;
|
||||
|
||||
TRACE_MGMT_DBG("Freeing being reinstated conn %p", conn);
|
||||
list_for_each_entry(c, &session->conn_list,
|
||||
conn_list_entry) {
|
||||
|
||||
@@ -76,6 +76,7 @@ static __be32 evaluate_crc32_from_sg(struct scatterlist *sg, int nbytes,
|
||||
|
||||
while (nbytes > 0) {
|
||||
int d = min(nbytes, (int)(sg->length));
|
||||
|
||||
crc = crc32c(crc, sg_virt(sg), d);
|
||||
nbytes -= d;
|
||||
sg++;
|
||||
|
||||
@@ -138,6 +138,7 @@ static inline int cmnd_read_size(struct iscsi_cmnd *cmnd)
|
||||
if (ahdr != NULL) {
|
||||
uint8_t *p = (uint8_t *)ahdr;
|
||||
unsigned int size = 0;
|
||||
|
||||
do {
|
||||
int s;
|
||||
|
||||
@@ -477,6 +478,7 @@ void cmnd_done(struct iscsi_cmnd *cmnd)
|
||||
{
|
||||
/* It can be for some aborted commands */
|
||||
struct scst_cmd *scst_cmd = cmnd->scst_cmd;
|
||||
|
||||
TRACE_DBG("cmd %p AFTER_PREPROC", cmnd);
|
||||
cmnd->scst_state = ISCSI_CMD_STATE_RESTARTED;
|
||||
cmnd->scst_cmd = NULL;
|
||||
@@ -1095,6 +1097,7 @@ static void iscsi_tcp_send_data_rsp(struct iscsi_cmnd *req, u8 *sense,
|
||||
send_data_rsp(req, status, is_send_status);
|
||||
} else {
|
||||
struct iscsi_cmnd *rsp;
|
||||
|
||||
send_data_rsp(req, 0, 0);
|
||||
if (is_send_status) {
|
||||
rsp = create_status_rsp(req, status, sense,
|
||||
@@ -1229,6 +1232,7 @@ static int create_reject_rsp(struct iscsi_cmnd *req, int reason, bool get_data)
|
||||
if (req->scst_cmd == NULL) {
|
||||
/* BUSY status must be already set */
|
||||
struct iscsi_scsi_rsp_hdr *rsp_hdr1;
|
||||
|
||||
rsp_hdr1 = (struct iscsi_scsi_rsp_hdr *)&req->main_rsp->pdu.bhs;
|
||||
sBUG_ON(rsp_hdr1->cmd_status == 0);
|
||||
/*
|
||||
@@ -1531,6 +1535,7 @@ int iscsi_preliminary_complete(struct iscsi_cmnd *req,
|
||||
#ifdef CONFIG_SCST_DEBUG
|
||||
{
|
||||
struct iscsi_hdr *req_hdr = &req->pdu.bhs;
|
||||
|
||||
TRACE_DBG_FLAG(iscsi_get_flow_ctrl_or_mgmt_dbg_log_flag(orig_req),
|
||||
"Prelim completed req %p, orig_req %p (FINAL %x, "
|
||||
"outstanding_r2t %d)", req, orig_req,
|
||||
@@ -1748,8 +1753,7 @@ out:
|
||||
static int iscsi_pre_exec(struct scst_cmd *scst_cmd)
|
||||
{
|
||||
int res = SCST_PREPROCESS_STATUS_SUCCESS;
|
||||
struct iscsi_cmnd *req = (struct iscsi_cmnd *)
|
||||
scst_cmd_get_tgt_priv(scst_cmd);
|
||||
struct iscsi_cmnd *req = scst_cmd_get_tgt_priv(scst_cmd);
|
||||
struct iscsi_cmnd *c, *t;
|
||||
|
||||
TRACE_ENTRY();
|
||||
@@ -2055,6 +2059,7 @@ static int scsi_cmnd_start(struct iscsi_cmnd *req)
|
||||
if ((req_hdr->flags & ISCSI_CMD_READ) &&
|
||||
(req_hdr->flags & ISCSI_CMD_WRITE)) {
|
||||
int sz = cmnd_read_size(req);
|
||||
|
||||
if (unlikely(sz < 0)) {
|
||||
PRINT_ERROR("%s", "BIDI data transfer, but initiator "
|
||||
"not supplied Bidirectional Read Expected Data "
|
||||
@@ -2117,6 +2122,7 @@ static int scsi_cmnd_start(struct iscsi_cmnd *req)
|
||||
if (ahdr != NULL) {
|
||||
uint8_t *p = (uint8_t *)ahdr;
|
||||
unsigned int size = 0;
|
||||
|
||||
do {
|
||||
int s;
|
||||
|
||||
@@ -2517,6 +2523,7 @@ static void iscsi_cmnd_abort_fn(struct work_struct *work)
|
||||
*/
|
||||
list_for_each_entry(conn, &session->conn_list, conn_list_entry) {
|
||||
struct iscsi_cmnd *c;
|
||||
|
||||
spin_lock_bh(&conn->cmd_list_lock);
|
||||
list_for_each_entry(c, &conn->cmd_list, cmd_list_entry) {
|
||||
if (c == cmnd) {
|
||||
@@ -3376,8 +3383,7 @@ static void iscsi_tcp_preprocessing_done(struct iscsi_cmnd *req)
|
||||
|
||||
static void iscsi_preprocessing_done(struct scst_cmd *scst_cmd)
|
||||
{
|
||||
struct iscsi_cmnd *req = (struct iscsi_cmnd *)
|
||||
scst_cmd_get_tgt_priv(scst_cmd);
|
||||
struct iscsi_cmnd *req = scst_cmd_get_tgt_priv(scst_cmd);
|
||||
|
||||
req->conn->transport->iscsit_preprocessing_done(req);
|
||||
}
|
||||
@@ -3489,8 +3495,7 @@ static void iscsi_tcp_conn_close(struct iscsi_conn *conn, int flags)
|
||||
static int iscsi_xmit_response(struct scst_cmd *scst_cmd)
|
||||
{
|
||||
int is_send_status = scst_cmd_get_is_send_status(scst_cmd);
|
||||
struct iscsi_cmnd *req = (struct iscsi_cmnd *)
|
||||
scst_cmd_get_tgt_priv(scst_cmd);
|
||||
struct iscsi_cmnd *req = scst_cmd_get_tgt_priv(scst_cmd);
|
||||
struct iscsi_conn *conn = req->conn;
|
||||
int status = scst_cmd_get_status(scst_cmd);
|
||||
u8 *sense = scst_cmd_get_sense_buffer(scst_cmd);
|
||||
@@ -3574,6 +3579,7 @@ static int iscsi_xmit_response(struct scst_cmd *scst_cmd)
|
||||
is_send_status);
|
||||
} else if (is_send_status) {
|
||||
struct iscsi_cmnd *rsp;
|
||||
|
||||
rsp = create_status_rsp(req, status, sense, sense_len);
|
||||
iscsi_cmnd_init_write(rsp, 0);
|
||||
}
|
||||
@@ -3768,8 +3774,7 @@ static inline int iscsi_get_mgmt_response(int status)
|
||||
static void iscsi_task_mgmt_fn_done(struct scst_mgmt_cmd *scst_mcmd)
|
||||
{
|
||||
int fn = scst_mgmt_cmd_get_fn(scst_mcmd);
|
||||
struct iscsi_cmnd *req = (struct iscsi_cmnd *)
|
||||
scst_mgmt_cmd_get_tgt_priv(scst_mcmd);
|
||||
struct iscsi_cmnd *req = scst_mgmt_cmd_get_tgt_priv(scst_mcmd);
|
||||
int status = iscsi_get_mgmt_response(scst_mgmt_cmd_get_status(scst_mcmd));
|
||||
|
||||
if ((status == ISCSI_RESPONSE_UNKNOWN_TASK) &&
|
||||
|
||||
@@ -693,6 +693,7 @@ static inline bool cmnd_get_check(struct iscsi_cmnd *cmnd)
|
||||
{
|
||||
int r = atomic_inc_return(&cmnd->ref_cnt);
|
||||
int res;
|
||||
|
||||
if (unlikely(r == 1)) {
|
||||
TRACE_DBG("cmnd %p is being destroyed", cmnd);
|
||||
atomic_dec(&cmnd->ref_cnt);
|
||||
|
||||
@@ -284,7 +284,7 @@ void isert_conn_free(struct isert_connection *isert_conn);
|
||||
void isert_conn_disconnect(struct isert_connection *isert_conn);
|
||||
void isert_post_drain(struct isert_connection *isert_conn);
|
||||
|
||||
static inline struct isert_connection *isert_conn_alloc(void)
|
||||
static inline struct isert_connection *isert_conn_zalloc(void)
|
||||
{
|
||||
return kmem_cache_zalloc(isert_conn_cache, GFP_KERNEL);
|
||||
}
|
||||
|
||||
@@ -49,8 +49,8 @@ static int isert_buf_alloc_pg(struct ib_device *ib_dev,
|
||||
struct page *page;
|
||||
|
||||
isert_buf->sg_cnt = DIV_ROUND_UP(size, PAGE_SIZE);
|
||||
isert_buf->sg = kmalloc(sizeof(*isert_buf->sg) * isert_buf->sg_cnt,
|
||||
GFP_KERNEL);
|
||||
isert_buf->sg = kmalloc_array(isert_buf->sg_cnt, sizeof(*isert_buf->sg),
|
||||
GFP_KERNEL);
|
||||
if (unlikely(!isert_buf->sg)) {
|
||||
pr_err("Failed to allocate buffer SG\n");
|
||||
res = -ENOMEM;
|
||||
@@ -294,6 +294,7 @@ out:
|
||||
void isert_wr_release(struct isert_wr *wr)
|
||||
{
|
||||
struct isert_buf *isert_buf = wr->buf;
|
||||
|
||||
if (isert_buf && isert_buf->is_alloced) {
|
||||
struct isert_device *isert_dev = wr->isert_dev;
|
||||
struct ib_device *ib_dev;
|
||||
|
||||
@@ -63,7 +63,8 @@ int isert_get_peer_addr(struct iscsi_conn *iscsi_conn, struct sockaddr *sa,
|
||||
size_t *addr_len)
|
||||
{
|
||||
int ret;
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
|
||||
struct isert_connection *isert_conn = container_of(iscsi_conn,
|
||||
struct isert_connection, iscsi);
|
||||
struct sockaddr *peer_sa = (struct sockaddr *)&isert_conn->peer_addr;
|
||||
|
||||
ret = isert_get_addr_size(peer_sa, addr_len);
|
||||
@@ -79,7 +80,8 @@ int isert_get_target_addr(struct iscsi_conn *iscsi_conn, struct sockaddr *sa,
|
||||
size_t *addr_len)
|
||||
{
|
||||
int ret;
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
|
||||
struct isert_connection *isert_conn = container_of(iscsi_conn,
|
||||
struct isert_connection, iscsi);
|
||||
struct sockaddr *self_sa = (struct sockaddr *)&isert_conn->self_addr;
|
||||
|
||||
ret = isert_get_addr_size(self_sa, addr_len);
|
||||
@@ -106,14 +108,17 @@ int isert_portal_remove(void *portal_h)
|
||||
|
||||
void isert_free_connection(struct iscsi_conn *iscsi_conn)
|
||||
{
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
|
||||
struct isert_connection *isert_conn = container_of(iscsi_conn,
|
||||
struct isert_connection, iscsi);
|
||||
|
||||
isert_post_drain(isert_conn);
|
||||
isert_conn_free(isert_conn);
|
||||
}
|
||||
|
||||
struct iscsi_cmnd *isert_alloc_login_rsp_pdu(struct iscsi_conn *iscsi_conn)
|
||||
{
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
|
||||
struct isert_connection *isert_conn = container_of(iscsi_conn,
|
||||
struct isert_connection, iscsi);
|
||||
struct isert_cmnd *isert_pdu = isert_conn->login_rsp_pdu;
|
||||
|
||||
isert_tx_pdu_init(isert_pdu, isert_conn);
|
||||
@@ -123,7 +128,8 @@ struct iscsi_cmnd *isert_alloc_login_rsp_pdu(struct iscsi_conn *iscsi_conn)
|
||||
static struct iscsi_cmnd *isert_alloc_scsi_pdu(struct iscsi_conn *iscsi_conn,
|
||||
int fake)
|
||||
{
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
|
||||
struct isert_connection *isert_conn = container_of(iscsi_conn,
|
||||
struct isert_connection, iscsi);
|
||||
struct isert_cmnd *isert_pdu;
|
||||
|
||||
again:
|
||||
@@ -153,8 +159,10 @@ struct iscsi_cmnd *isert_alloc_scsi_fake_pdu(struct iscsi_conn *iscsi_conn)
|
||||
|
||||
void isert_release_tx_pdu(struct iscsi_cmnd *iscsi_pdu)
|
||||
{
|
||||
struct isert_cmnd *isert_pdu = (struct isert_cmnd *)iscsi_pdu;
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_pdu->conn;
|
||||
struct isert_cmnd *isert_pdu = container_of(iscsi_pdu,
|
||||
struct isert_cmnd, iscsi);
|
||||
struct isert_connection *isert_conn = container_of(iscsi_pdu->conn,
|
||||
struct isert_connection, iscsi);
|
||||
|
||||
isert_tx_pdu_init_iscsi(isert_pdu);
|
||||
|
||||
@@ -165,16 +173,17 @@ void isert_release_tx_pdu(struct iscsi_cmnd *iscsi_pdu)
|
||||
|
||||
void isert_release_rx_pdu(struct iscsi_cmnd *iscsi_pdu)
|
||||
{
|
||||
struct isert_cmnd *isert_pdu = (struct isert_cmnd *)iscsi_pdu;
|
||||
struct isert_cmnd *isert_pdu = container_of(iscsi_pdu,
|
||||
struct isert_cmnd, iscsi);
|
||||
|
||||
if (likely(!isert_pdu->is_fake_rx))
|
||||
isert_rx_pdu_done(isert_pdu);
|
||||
isert_rx_pdu_done(isert_pdu);
|
||||
}
|
||||
|
||||
/* if last transition into FF (Fully Featured) state */
|
||||
int isert_login_rsp_tx(struct iscsi_cmnd *login_rsp, int last, int discovery)
|
||||
{
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)login_rsp->conn;
|
||||
struct isert_connection *isert_conn = container_of(login_rsp->conn,
|
||||
struct isert_connection, iscsi);
|
||||
int err;
|
||||
|
||||
if (last && !discovery) {
|
||||
@@ -202,7 +211,8 @@ int isert_set_session_params(struct iscsi_conn *iscsi_conn,
|
||||
struct iscsi_sess_params *sess_params,
|
||||
struct iscsi_tgt_params *tgt_params)
|
||||
{
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
|
||||
struct isert_connection *isert_conn = container_of(iscsi_conn,
|
||||
struct isert_connection, iscsi);
|
||||
|
||||
isert_conn->queue_depth = tgt_params->queued_cmnds;
|
||||
|
||||
@@ -217,8 +227,10 @@ int isert_set_session_params(struct iscsi_conn *iscsi_conn,
|
||||
|
||||
int isert_pdu_tx(struct iscsi_cmnd *iscsi_cmnd)
|
||||
{
|
||||
struct isert_cmnd *isert_cmnd = (struct isert_cmnd *)iscsi_cmnd;
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_cmnd->conn;
|
||||
struct isert_cmnd *isert_cmnd = container_of(iscsi_cmnd,
|
||||
struct isert_cmnd, iscsi);
|
||||
struct isert_connection *isert_conn = container_of(iscsi_cmnd->conn,
|
||||
struct isert_connection, iscsi);
|
||||
int err;
|
||||
|
||||
isert_tx_pdu_convert_from_iscsi(isert_cmnd, iscsi_cmnd);
|
||||
@@ -229,8 +241,10 @@ int isert_pdu_tx(struct iscsi_cmnd *iscsi_cmnd)
|
||||
|
||||
int isert_request_data_out(struct iscsi_cmnd *iscsi_cmnd)
|
||||
{
|
||||
struct isert_cmnd *isert_cmnd = (struct isert_cmnd *)iscsi_cmnd;
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_cmnd->conn;
|
||||
struct isert_cmnd *isert_cmnd = container_of(iscsi_cmnd,
|
||||
struct isert_cmnd, iscsi);
|
||||
struct isert_connection *isert_conn = container_of(iscsi_cmnd->conn,
|
||||
struct isert_connection, iscsi);
|
||||
int ret;
|
||||
|
||||
ret = isert_prepare_rdma(isert_cmnd, isert_conn, ISER_WR_RDMA_READ);
|
||||
@@ -245,9 +259,12 @@ int isert_request_data_out(struct iscsi_cmnd *iscsi_cmnd)
|
||||
int isert_send_data_in(struct iscsi_cmnd *iscsi_cmnd,
|
||||
struct iscsi_cmnd *iscsi_rsp)
|
||||
{
|
||||
struct isert_cmnd *isert_cmnd = (struct isert_cmnd *)iscsi_cmnd;
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_cmnd->conn;
|
||||
struct isert_cmnd *isert_rsp = (struct isert_cmnd *)iscsi_rsp;
|
||||
struct isert_cmnd *isert_cmnd = container_of(iscsi_cmnd,
|
||||
struct isert_cmnd, iscsi);
|
||||
struct isert_connection *isert_conn = container_of(iscsi_cmnd->conn,
|
||||
struct isert_connection, iscsi);
|
||||
struct isert_cmnd *isert_rsp = container_of(iscsi_rsp,
|
||||
struct isert_cmnd, iscsi);
|
||||
int ret;
|
||||
|
||||
ret = isert_prepare_rdma(isert_cmnd, isert_conn, ISER_WR_RDMA_WRITE);
|
||||
@@ -262,7 +279,8 @@ int isert_send_data_in(struct iscsi_cmnd *iscsi_cmnd,
|
||||
|
||||
int isert_close_connection(struct iscsi_conn *iscsi_conn)
|
||||
{
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
|
||||
struct isert_connection *isert_conn = container_of(iscsi_conn,
|
||||
struct isert_connection, iscsi);
|
||||
|
||||
isert_conn_disconnect(isert_conn);
|
||||
|
||||
@@ -276,14 +294,16 @@ int isert_task_abort(struct iscsi_cmnd *cmnd)
|
||||
|
||||
void *isert_get_priv(struct iscsi_conn *iscsi_conn)
|
||||
{
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
|
||||
struct isert_connection *isert_conn = container_of(iscsi_conn,
|
||||
struct isert_connection, iscsi);
|
||||
|
||||
return isert_conn->priv_data;
|
||||
}
|
||||
|
||||
void isert_set_priv(struct iscsi_conn *iscsi_conn, void *priv)
|
||||
{
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
|
||||
struct isert_connection *isert_conn = container_of(iscsi_conn,
|
||||
struct isert_connection, iscsi);
|
||||
|
||||
isert_conn->priv_data = priv;
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ static int isert_rx_pdu_init(struct isert_cmnd *isert_pdu,
|
||||
{
|
||||
struct iscsi_cmnd *iscsi_cmnd = &isert_pdu->iscsi;
|
||||
int err = isert_pdu_rx_buf_init(isert_pdu, isert_conn);
|
||||
|
||||
if (unlikely(err < 0))
|
||||
return err;
|
||||
iscsi_cmnd->conn = &isert_conn->iscsi;
|
||||
@@ -174,14 +175,14 @@ static int isert_alloc_for_rdma(struct isert_cmnd *pdu, int sge_cnt,
|
||||
int i, ret = 0;
|
||||
int wr_cnt;
|
||||
|
||||
sg_pool = kmalloc(sizeof(*sg_pool) * sge_cnt, GFP_KERNEL);
|
||||
sg_pool = kmalloc_array(sge_cnt, sizeof(*sg_pool), GFP_KERNEL);
|
||||
if (unlikely(sg_pool == NULL)) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
wr_cnt = DIV_ROUND_UP(sge_cnt, isert_conn->max_sge);
|
||||
wr = kmalloc(sizeof(*wr) * wr_cnt, GFP_KERNEL);
|
||||
wr = kmalloc_array(wr_cnt, sizeof(*wr), GFP_KERNEL);
|
||||
if (unlikely(wr == NULL)) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free_sg_pool;
|
||||
@@ -478,7 +479,8 @@ clean_pdus:
|
||||
|
||||
static int isert_reinit_rx_pdu(struct isert_cmnd *pdu)
|
||||
{
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)pdu->iscsi.conn;
|
||||
struct isert_connection *isert_conn = container_of(pdu->iscsi.conn,
|
||||
struct isert_connection, iscsi);
|
||||
|
||||
pdu->is_rstag_valid = 0;
|
||||
pdu->is_wstag_valid = 0;
|
||||
@@ -491,7 +493,8 @@ static int isert_reinit_rx_pdu(struct isert_cmnd *pdu)
|
||||
int isert_rx_pdu_done(struct isert_cmnd *pdu)
|
||||
{
|
||||
int err;
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)pdu->iscsi.conn;
|
||||
struct isert_connection *isert_conn = container_of(pdu->iscsi.conn,
|
||||
struct isert_connection, iscsi);
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
|
||||
@@ -136,8 +136,10 @@ void isert_post_drain(struct isert_connection *isert_conn)
|
||||
err = ib_post_send(isert_conn->qp, &isert_conn->drain_wr.send_wr, &bad_wr);
|
||||
if (unlikely(err)) {
|
||||
pr_err("Failed to post drain wr, err:%d\n", err);
|
||||
/* We need to decrement iser_conn->kref in order to be able to cleanup
|
||||
* the connection */
|
||||
/*
|
||||
* We need to decrement iser_conn->kref in order to be
|
||||
* able to cleanup the connection.
|
||||
*/
|
||||
set_bit(ISERT_DRAIN_FAILED, &isert_conn->flags);
|
||||
isert_conn_free(isert_conn);
|
||||
}
|
||||
@@ -352,7 +354,9 @@ static void isert_send_completion_handler(struct isert_wr *wr)
|
||||
struct isert_cmnd *isert_pdu = wr->pdu;
|
||||
struct iscsi_cmnd *iscsi_pdu = &isert_pdu->iscsi;
|
||||
struct iscsi_cmnd *iscsi_req_pdu = iscsi_pdu->parent_req;
|
||||
struct isert_cmnd *isert_req_pdu = (struct isert_cmnd *)iscsi_req_pdu;
|
||||
struct isert_cmnd *isert_req_pdu = container_of(iscsi_req_pdu,
|
||||
struct isert_cmnd, iscsi);
|
||||
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
@@ -633,9 +637,11 @@ static void isert_handle_wc_error(struct ib_wc *wc)
|
||||
isert_buf->dma_dir);
|
||||
isert_buf->sg_cnt = 0;
|
||||
}
|
||||
/* RDMA-WR and SEND response of a READ task
|
||||
are sent together, so when receiving RDMA-WR error,
|
||||
wait until SEND error arrives to complete the task */
|
||||
/*
|
||||
* RDMA-WR and SEND response of a READ task
|
||||
* are sent together, so when receiving RDMA-WR error,
|
||||
* wait until SEND error arrives to complete the task.
|
||||
*/
|
||||
break;
|
||||
default:
|
||||
pr_err("unexpected opcode %d, wc:%p wr_id:%p conn:%p\n",
|
||||
@@ -866,7 +872,8 @@ static struct isert_device *isert_device_create(struct ib_device *ib_dev)
|
||||
isert_dev->num_cqs = min_t(int, num_online_cpus(),
|
||||
ib_dev->num_comp_vectors);
|
||||
|
||||
isert_dev->cq_qps = kzalloc(sizeof(*isert_dev->cq_qps) * isert_dev->num_cqs,
|
||||
isert_dev->cq_qps = kcalloc(isert_dev->num_cqs,
|
||||
sizeof(*isert_dev->cq_qps),
|
||||
GFP_KERNEL);
|
||||
if (unlikely(isert_dev->cq_qps == NULL)) {
|
||||
pr_err("Failed to allocate iser cq_qps\n");
|
||||
@@ -1142,7 +1149,7 @@ static struct isert_connection *isert_conn_create(struct rdma_cm_id *cm_id,
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
isert_conn = isert_conn_alloc();
|
||||
isert_conn = isert_conn_zalloc();
|
||||
if (unlikely(!isert_conn)) {
|
||||
pr_err("Unable to allocate iser conn, cm_id:%p\n", cm_id);
|
||||
err = -ENOMEM;
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#endif
|
||||
#include "isert_dbg.h"
|
||||
#include "isert.h"
|
||||
#include "iser.h"
|
||||
#include "iser_datamover.h"
|
||||
|
||||
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
|
||||
@@ -190,6 +191,9 @@ static struct iscsi_cmnd *isert_cmnd_alloc(struct iscsi_conn *conn,
|
||||
|
||||
static void isert_cmnd_free(struct iscsi_cmnd *cmnd)
|
||||
{
|
||||
struct isert_cmnd *isert_cmnd = container_of(cmnd, struct isert_cmnd,
|
||||
iscsi);
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
#ifdef CONFIG_SCST_EXTRACHECKS
|
||||
@@ -211,7 +215,7 @@ static void isert_cmnd_free(struct iscsi_cmnd *cmnd)
|
||||
sBUG();
|
||||
}
|
||||
#endif
|
||||
if (cmnd->parent_req)
|
||||
if (cmnd->parent_req || isert_cmnd->is_fake_rx)
|
||||
isert_release_tx_pdu(cmnd);
|
||||
else
|
||||
isert_release_rx_pdu(cmnd);
|
||||
@@ -282,9 +286,11 @@ static void isert_free_conn(struct iscsi_conn *conn)
|
||||
int isert_handle_close_connection(struct iscsi_conn *conn)
|
||||
{
|
||||
isert_mark_conn_closed(conn, 0);
|
||||
/* Take care of case where our connection is being closed
|
||||
* without being connected to a session - if connection allocation
|
||||
* failed for some reason */
|
||||
/*
|
||||
* Take care of case where our connection is being closed without
|
||||
* being connected to a session - if connection allocation failed for
|
||||
* some reason.
|
||||
*/
|
||||
if (unlikely(!conn->session))
|
||||
isert_free_connection(conn);
|
||||
else
|
||||
|
||||
@@ -567,6 +567,7 @@ static ssize_t isert_read(struct file *filp, char __user *buf, size_t count,
|
||||
|
||||
if (will_read_block(dev)) {
|
||||
int ret;
|
||||
|
||||
if (filp->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
ret = wait_event_freezable(dev->waitqueue,
|
||||
@@ -804,11 +805,8 @@ int isert_login_req_rx(struct iscsi_cmnd *login_req)
|
||||
switch (dev->state) {
|
||||
case CS_INIT:
|
||||
case CS_RSP_FINISHED:
|
||||
if (unlikely(dev->login_req != NULL)) {
|
||||
if (unlikely(dev->login_req != NULL))
|
||||
sBUG();
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case CS_REQ_BHS: /* Got login request before done handling old one */
|
||||
@@ -825,8 +823,6 @@ int isert_login_req_rx(struct iscsi_cmnd *login_req)
|
||||
|
||||
default:
|
||||
sBUG();
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
@@ -956,7 +952,7 @@ int __init isert_init_login_devs(unsigned int ndevs)
|
||||
* allocate the devices -- we can't have them static, as the number
|
||||
* can be specified at load time
|
||||
*/
|
||||
isert_conn_devices = kzalloc(n_devs * sizeof(struct isert_conn_dev),
|
||||
isert_conn_devices = kcalloc(n_devs, sizeof(struct isert_conn_dev),
|
||||
GFP_KERNEL);
|
||||
if (unlikely(!isert_conn_devices)) {
|
||||
res = -ENOMEM;
|
||||
|
||||
@@ -100,6 +100,7 @@ again:
|
||||
|
||||
for (i = 0; i < cmnd->sg_cnt; i++) {
|
||||
struct page *page = sg_page(&cmnd->sg[i]);
|
||||
|
||||
TRACE_CONN_CLOSE_DBG("page %p, net_priv %p, "
|
||||
"_count %d", page, page->net_priv,
|
||||
atomic_read(&page->_count));
|
||||
@@ -282,8 +283,10 @@ static void trace_conn_close(struct iscsi_conn *conn)
|
||||
cmnd->sg);
|
||||
if (cmnd->sg != NULL) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cmnd->sg_cnt; i++) {
|
||||
struct page *page = sg_page(&cmnd->sg[i]);
|
||||
|
||||
TRACE_CONN_CLOSE_DBG("page %p, "
|
||||
"net_priv %p, _count %d",
|
||||
page, page->net_priv,
|
||||
@@ -301,6 +304,7 @@ static void trace_conn_close(struct iscsi_conn *conn)
|
||||
atomic_read(&rsp->net_ref_cnt), rsp->sg);
|
||||
if (rsp->sg != cmnd->sg && rsp->sg) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < rsp->sg_cnt; i++) {
|
||||
TRACE_CONN_CLOSE_DBG(" page %p, "
|
||||
"net_priv %p, _count %d",
|
||||
@@ -330,7 +334,7 @@ void iscsi_task_mgmt_affected_cmds_done(struct scst_mgmt_cmd *scst_mcmd)
|
||||
switch (fn) {
|
||||
case SCST_NEXUS_LOSS_SESS:
|
||||
{
|
||||
struct iscsi_conn *conn = (struct iscsi_conn *)priv;
|
||||
struct iscsi_conn *conn = priv;
|
||||
struct iscsi_session *sess = conn->session;
|
||||
struct iscsi_conn *c;
|
||||
|
||||
@@ -459,6 +463,7 @@ static void close_conn(struct iscsi_conn *conn)
|
||||
spin_lock(&session->sn_lock);
|
||||
if (session->tm_rsp && session->tm_rsp->conn == conn) {
|
||||
struct iscsi_cmnd *tm_rsp = session->tm_rsp;
|
||||
|
||||
iscsi_drop_delayed_tm_rsp(tm_rsp);
|
||||
spin_unlock(&session->sn_lock);
|
||||
mutex_unlock(&target->target_mutex);
|
||||
@@ -579,7 +584,7 @@ static void close_conn(struct iscsi_conn *conn)
|
||||
|
||||
static int close_conn_thr(void *arg)
|
||||
{
|
||||
struct iscsi_conn *conn = (struct iscsi_conn *)arg;
|
||||
struct iscsi_conn *conn = arg;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
@@ -789,6 +794,7 @@ static int iscsi_rx_check_ddigest(struct iscsi_conn *conn)
|
||||
res = digest_rx_data(cmnd);
|
||||
if (unlikely(res != 0)) {
|
||||
struct iscsi_cmnd *orig_req;
|
||||
|
||||
if (cmnd_opcode(cmnd) == ISCSI_OP_SCSI_DATA_OUT)
|
||||
orig_req = cmnd->cmd_req;
|
||||
else
|
||||
@@ -908,6 +914,7 @@ static int process_read_io(struct iscsi_conn *conn, int *closed)
|
||||
res = do_recv(conn);
|
||||
if (res == 0) {
|
||||
int psz = ((cmnd->pdu.datasize + 3) & -4) - cmnd->pdu.datasize;
|
||||
|
||||
if (psz != 0) {
|
||||
TRACE_DBG("padding %d bytes", psz);
|
||||
iscsi_conn_init_read(conn,
|
||||
@@ -1132,7 +1139,7 @@ static inline void __iscsi_get_page_callback(struct iscsi_cmnd *cmd)
|
||||
|
||||
void iscsi_get_page_callback(struct page *page)
|
||||
{
|
||||
struct iscsi_cmnd *cmd = (struct iscsi_cmnd *)page->net_priv;
|
||||
struct iscsi_cmnd *cmd = page->net_priv;
|
||||
|
||||
TRACE_NET_PAGE("page %p, _count %d", page,
|
||||
atomic_read(&page->_count));
|
||||
@@ -1148,8 +1155,10 @@ static inline void __iscsi_put_page_callback(struct iscsi_cmnd *cmd)
|
||||
|
||||
if (atomic_dec_and_test(&cmd->net_ref_cnt)) {
|
||||
int i, sg_cnt = cmd->sg_cnt;
|
||||
|
||||
for (i = 0; i < sg_cnt; i++) {
|
||||
struct page *page = sg_page(&cmd->sg[i]);
|
||||
|
||||
TRACE_NET_PAGE("Clearing page %p", page);
|
||||
if (page->net_priv == cmd)
|
||||
page->net_priv = NULL;
|
||||
@@ -1161,7 +1170,7 @@ static inline void __iscsi_put_page_callback(struct iscsi_cmnd *cmd)
|
||||
|
||||
void iscsi_put_page_callback(struct page *page)
|
||||
{
|
||||
struct iscsi_cmnd *cmd = (struct iscsi_cmnd *)page->net_priv;
|
||||
struct iscsi_cmnd *cmd = page->net_priv;
|
||||
|
||||
TRACE_NET_PAGE("page %p, _count %d", page,
|
||||
atomic_read(&page->_count));
|
||||
@@ -1216,9 +1225,11 @@ void req_add_to_write_timeout_list(struct iscsi_cmnd *req)
|
||||
unsigned long req_tt = iscsi_get_timeout_time(req);
|
||||
struct iscsi_cmnd *r;
|
||||
bool inserted = false;
|
||||
|
||||
list_for_each_entry(r, &conn->write_timeout_list,
|
||||
write_timeout_list_entry) {
|
||||
unsigned long tt = iscsi_get_timeout_time(r);
|
||||
|
||||
if (time_after(tt, req_tt)) {
|
||||
TRACE_DBG("Add NOP IN req %p (tt %ld) before "
|
||||
"req %p (tt %ld)", req, req_tt, r, tt);
|
||||
@@ -1250,6 +1261,7 @@ void req_add_to_write_timeout_list(struct iscsi_cmnd *req)
|
||||
|
||||
if (!timer_pending(&conn->rsp_timer)) {
|
||||
unsigned long timeout_time;
|
||||
|
||||
if (unlikely(conn->conn_tm_active ||
|
||||
test_bit(ISCSI_CMD_ABORTED,
|
||||
&req->prelim_compl_flags))) {
|
||||
@@ -1270,6 +1282,7 @@ void req_add_to_write_timeout_list(struct iscsi_cmnd *req)
|
||||
&req->prelim_compl_flags))) {
|
||||
unsigned long timeout_time = jiffies +
|
||||
ISCSI_TM_DATA_WAIT_TIMEOUT + ISCSI_ADD_SCHED_TIME;
|
||||
|
||||
set_conn_tm_active = true;
|
||||
if (time_after(conn->rsp_timer.expires, timeout_time)) {
|
||||
TRACE_MGMT_DBG("Mod timer on %ld (conn %p)",
|
||||
@@ -1448,6 +1461,7 @@ retry:
|
||||
#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION)
|
||||
{
|
||||
static DEFINE_SPINLOCK(net_priv_lock);
|
||||
|
||||
spin_lock(&net_priv_lock);
|
||||
if (unlikely(page->net_priv != NULL)) {
|
||||
if (page->net_priv != ref_cmd) {
|
||||
|
||||
@@ -199,6 +199,7 @@ int __add_session(struct iscsi_target *target,
|
||||
list_for_each_entry_reverse(sess, &target->session_list,
|
||||
session_list_entry) {
|
||||
union iscsi_sid s = *(union iscsi_sid *)&sess->sid;
|
||||
|
||||
s.id.tsih = 0;
|
||||
if ((sid.id64 == s.id64) &&
|
||||
(strcmp(info->initiator_name, sess->initiator_name) == 0)) {
|
||||
@@ -332,6 +333,7 @@ int session_free(struct iscsi_session *session, bool del)
|
||||
|
||||
if (session->sess_reinstating) {
|
||||
struct iscsi_session *s;
|
||||
|
||||
TRACE_MGMT_DBG("Freeing being reinstated sess %p", session);
|
||||
list_for_each_entry(s, &session->target->session_list,
|
||||
session_list_entry) {
|
||||
|
||||
@@ -298,6 +298,7 @@ void target_del_session(struct iscsi_target *target,
|
||||
|
||||
if (!list_empty(&session->conn_list)) {
|
||||
struct iscsi_conn *conn, *tc;
|
||||
|
||||
list_for_each_entry_safe(conn, tc, &session->conn_list,
|
||||
conn_list_entry) {
|
||||
TRACE_MGMT_DBG("Del session: closing conn %p", conn);
|
||||
@@ -616,6 +617,7 @@ ssize_t iscsi_sysfs_del_target(const char *target_name)
|
||||
/* We don't want to have tgt visible after the mutex unlock */
|
||||
{
|
||||
struct iscsi_target *tgt;
|
||||
|
||||
mutex_lock(&target_mgmt_mutex);
|
||||
tgt = target_lookup_by_name(target_name);
|
||||
if (tgt == NULL) {
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
ABT_DETAILS="x86_64"
|
||||
ABT_JOBS=5
|
||||
ABT_KERNELS=" \
|
||||
4.2.5 \
|
||||
4.1.12-nc \
|
||||
4.3 \
|
||||
4.2.5-nc \
|
||||
4.1.12-nc \
|
||||
4.0.9-nc \
|
||||
3.19.7-nc \
|
||||
3.18.19-nc \
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
ccflags-y += -Idrivers/scsi/qla2xxx
|
||||
|
||||
qla2x00tgt-y := qla2x00t.o
|
||||
|
||||
obj-$(CONFIG_SCST_QLA_TGT_ADDON) += qla2x00tgt.o
|
||||
@@ -1,7 +1,7 @@
|
||||
Target driver for QLogic 2[2-6]xx/8[1-3]xx Fibre Channel cards
|
||||
==============================================================
|
||||
|
||||
Version 3.1.0, XX XXXXX 2015
|
||||
Version 3.2.0, XX XXXXX 2015
|
||||
----------------------------
|
||||
|
||||
This is target driver for QLogic 2[2-6]xx/8[1-3]xx Fibre Channel cards.
|
||||
@@ -20,7 +20,7 @@ necessary callbacks, but it's still capable to work as initiator only.
|
||||
Mode, when a host acts as the initiator and the target simultaneously,
|
||||
is supported as well.
|
||||
|
||||
This version is compatible with SCST core version 3.1.0 and higher and
|
||||
This version is compatible with SCST core version 3.2.0 and higher and
|
||||
Linux kernel 2.6.26 and higher. Sorry, kernels below 2.6.26 are not
|
||||
supported, because it's too hard to backport used initiator driver to
|
||||
older kernels.
|
||||
|
||||
@@ -308,6 +308,7 @@ static inline struct q2t_sess *q2t_find_sess_by_loop_id(struct q2t_tgt *tgt,
|
||||
uint16_t loop_id)
|
||||
{
|
||||
struct q2t_sess *sess;
|
||||
|
||||
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
|
||||
if (loop_id == sess->loop_id) {
|
||||
EXTRACHECKS_BUG_ON(sess->deleted);
|
||||
@@ -322,6 +323,7 @@ static inline struct q2t_sess *q2t_find_sess_by_s_id_include_deleted(
|
||||
struct q2t_tgt *tgt, const uint8_t *s_id)
|
||||
{
|
||||
struct q2t_sess *sess;
|
||||
|
||||
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
|
||||
if ((sess->s_id.b.al_pa == s_id[2]) &&
|
||||
(sess->s_id.b.area == s_id[1]) &&
|
||||
@@ -346,6 +348,7 @@ static inline struct q2t_sess *q2t_find_sess_by_s_id(struct q2t_tgt *tgt,
|
||||
const uint8_t *s_id)
|
||||
{
|
||||
struct q2t_sess *sess;
|
||||
|
||||
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
|
||||
if ((sess->s_id.b.al_pa == s_id[2]) &&
|
||||
(sess->s_id.b.area == s_id[1]) &&
|
||||
@@ -362,6 +365,7 @@ static inline struct q2t_sess *q2t_find_sess_by_s_id_le(struct q2t_tgt *tgt,
|
||||
const uint8_t *s_id)
|
||||
{
|
||||
struct q2t_sess *sess;
|
||||
|
||||
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
|
||||
if ((sess->s_id.b.al_pa == s_id[0]) &&
|
||||
(sess->s_id.b.area == s_id[1]) &&
|
||||
@@ -378,6 +382,7 @@ static inline struct q2t_sess *q2t_find_sess_by_port_name(struct q2t_tgt *tgt,
|
||||
const uint8_t *port_name)
|
||||
{
|
||||
struct q2t_sess *sess;
|
||||
|
||||
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
|
||||
if ((sess->port_name[0] == port_name[0]) &&
|
||||
(sess->port_name[1] == port_name[1]) &&
|
||||
@@ -399,6 +404,7 @@ static inline struct q2t_sess *q2t_find_sess_by_port_name_include_deleted(
|
||||
struct q2t_tgt *tgt, const uint8_t *port_name)
|
||||
{
|
||||
struct q2t_sess *sess;
|
||||
|
||||
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
|
||||
if ((sess->port_name[0] == port_name[0]) &&
|
||||
(sess->port_name[1] == port_name[1]) &&
|
||||
@@ -446,6 +452,7 @@ static inline int q2t_issue_marker(scsi_qla_host_t *vha, int vha_locked)
|
||||
/* Send marker if required */
|
||||
if (unlikely(vha->marker_needed != 0)) {
|
||||
int rc = qla2x00_issue_marker(vha, vha_locked);
|
||||
|
||||
if (rc != QLA_SUCCESS) {
|
||||
PRINT_ERROR("qla2x00t(%ld): issue_marker() "
|
||||
"failed", vha->host_no);
|
||||
@@ -468,6 +475,7 @@ static inline scsi_qla_host_t *q2t_find_host_by_d_id(scsi_qla_host_t *vha,
|
||||
|
||||
if (IS_FWI2_CAPABLE(ha)) {
|
||||
uint8_t vp_idx;
|
||||
|
||||
sBUG_ON(ha->tgt_vp_map == NULL);
|
||||
vp_idx = ha->tgt_vp_map[d_id[2]].idx;
|
||||
if (likely(test_bit(vp_idx, ha->vp_idx_map)))
|
||||
@@ -547,6 +555,7 @@ static void q24_try_to_dequeue_unknown_atios(struct qla_hw_data *ha)
|
||||
list_for_each_entry_safe(u, t, &ha->unknown_atio_list,
|
||||
unknown_atio_list_entry) {
|
||||
scsi_qla_host_t *host, *vha = u->vha;
|
||||
|
||||
sBUG_ON(vha->hw != ha);
|
||||
host = q2t_find_host_by_d_id(vha, u->atio7.fcp_hdr.d_id);
|
||||
if (host != NULL) {
|
||||
@@ -610,7 +619,8 @@ static void q24_atio_pkt_all_vps(scsi_qla_host_t *vha, atio7_entry_t *atio)
|
||||
case ATIO_TYPE7:
|
||||
{
|
||||
scsi_qla_host_t *host = q2t_find_host_by_d_id(vha, atio->fcp_hdr.d_id);
|
||||
if (unlikely(NULL == host)) {
|
||||
|
||||
if (unlikely(host == NULL)) {
|
||||
/*
|
||||
* It might happen, because there is a small gap between
|
||||
* requesting the DPC thread to update loop and actual
|
||||
@@ -628,8 +638,10 @@ static void q24_atio_pkt_all_vps(scsi_qla_host_t *vha, atio7_entry_t *atio)
|
||||
case IMMED_NOTIFY_TYPE:
|
||||
{
|
||||
scsi_qla_host_t *host = vha;
|
||||
|
||||
if (IS_FWI2_CAPABLE(ha)) {
|
||||
notify24xx_entry_t *entry = (notify24xx_entry_t *)atio;
|
||||
|
||||
if ((entry->vp_index != 0xFF) &&
|
||||
(entry->nport_handle != 0xFFFF)) {
|
||||
host = q2t_find_host_by_vp_idx(vha,
|
||||
@@ -671,6 +683,7 @@ static void q2t_response_pkt_all_vps(scsi_qla_host_t *vha, response_t *pkt)
|
||||
ctio7_fw_entry_t *entry = (ctio7_fw_entry_t *)pkt;
|
||||
scsi_qla_host_t *host = q2t_find_host_by_vp_idx(vha,
|
||||
entry->vp_index);
|
||||
|
||||
if (unlikely(!host)) {
|
||||
PRINT_ERROR("qla2x00t(%ld): Response pkt (CTIO_TYPE7) "
|
||||
"received, with unknown vp_index %d",
|
||||
@@ -684,8 +697,10 @@ static void q2t_response_pkt_all_vps(scsi_qla_host_t *vha, response_t *pkt)
|
||||
case IMMED_NOTIFY_TYPE:
|
||||
{
|
||||
scsi_qla_host_t *host = vha;
|
||||
|
||||
if (IS_FWI2_CAPABLE(ha)) {
|
||||
notify24xx_entry_t *entry = (notify24xx_entry_t *)pkt;
|
||||
|
||||
host = q2t_find_host_by_vp_idx(vha, entry->vp_index);
|
||||
if (unlikely(!host)) {
|
||||
PRINT_ERROR("qla2x00t(%ld): Response pkt "
|
||||
@@ -702,9 +717,11 @@ static void q2t_response_pkt_all_vps(scsi_qla_host_t *vha, response_t *pkt)
|
||||
case NOTIFY_ACK_TYPE:
|
||||
{
|
||||
scsi_qla_host_t *host = vha;
|
||||
|
||||
if (IS_FWI2_CAPABLE(ha)) {
|
||||
nack24xx_entry_t *entry = (nack24xx_entry_t *)pkt;
|
||||
if (0xFF != entry->vp_index) {
|
||||
|
||||
if (entry->vp_index != 0xFF) {
|
||||
host = q2t_find_host_by_vp_idx(vha,
|
||||
entry->vp_index);
|
||||
if (unlikely(!host)) {
|
||||
@@ -726,6 +743,7 @@ static void q2t_response_pkt_all_vps(scsi_qla_host_t *vha, response_t *pkt)
|
||||
abts24_recv_entry_t *entry = (abts24_recv_entry_t *)pkt;
|
||||
scsi_qla_host_t *host = q2t_find_host_by_vp_idx(vha,
|
||||
entry->vp_index);
|
||||
|
||||
if (unlikely(!host)) {
|
||||
PRINT_ERROR("qla2x00t(%ld): Response pkt "
|
||||
"(ABTS_RECV_24XX) received, with unknown "
|
||||
@@ -741,6 +759,7 @@ static void q2t_response_pkt_all_vps(scsi_qla_host_t *vha, response_t *pkt)
|
||||
abts24_resp_entry_t *entry = (abts24_resp_entry_t *)pkt;
|
||||
scsi_qla_host_t *host = q2t_find_host_by_vp_idx(vha,
|
||||
entry->vp_index);
|
||||
|
||||
if (unlikely(!host)) {
|
||||
PRINT_ERROR("qla2x00t(%ld): Response pkt "
|
||||
"(ABTS_RECV_24XX) received, with unknown "
|
||||
@@ -882,6 +901,7 @@ static int q2t_reset(scsi_qla_host_t *vha, void *iocb, int mcmd)
|
||||
|
||||
if (IS_FWI2_CAPABLE(ha)) {
|
||||
notify24xx_entry_t *n = (notify24xx_entry_t *)iocb;
|
||||
|
||||
if ((le16_to_cpu(n->status) == IMM_NTFY_ELS) &&
|
||||
((n->status_subcode == ELS_TPRLO) ||
|
||||
(n->status_subcode == ELS_LOGO))) {
|
||||
@@ -1154,6 +1174,7 @@ retry:
|
||||
res = -1;
|
||||
for (i = 0; i < entries; i++) {
|
||||
struct gid_list_info *gid = (struct gid_list_info *)id_iter;
|
||||
|
||||
if ((gid->al_pa == s_id[2]) &&
|
||||
(gid->area == s_id[1]) &&
|
||||
(gid->domain == s_id[0])) {
|
||||
@@ -2053,6 +2074,7 @@ static int __q24_handle_abts(scsi_qla_host_t *vha, abts24_recv_entry_t *abts,
|
||||
*/
|
||||
while (!list_empty(&ha->unknown_atio_list)) {
|
||||
struct q2t_unknown_atio *u;
|
||||
|
||||
u = list_first_entry(&ha->unknown_atio_list,
|
||||
struct q2t_unknown_atio, unknown_atio_list_entry);
|
||||
TRACE_MGMT_DBG("qla2x00t(%ld): Clearing unknown "
|
||||
@@ -3304,6 +3326,7 @@ static int __q2t_rdy_to_xfer(struct q2t_cmd *cmd)
|
||||
|
||||
if (IS_FWI2_CAPABLE(ha)) {
|
||||
ctio7_status0_entry_t *pkt;
|
||||
|
||||
res = q24_build_ctio_pkt(&prm);
|
||||
if (unlikely(res != SCST_TGT_RES_SUCCESS))
|
||||
goto out_unlock_free_unmap;
|
||||
@@ -3314,6 +3337,7 @@ static int __q2t_rdy_to_xfer(struct q2t_cmd *cmd)
|
||||
p = pkt;
|
||||
} else {
|
||||
ctio_common_entry_t *pkt;
|
||||
|
||||
q2x_build_ctio_pkt(&prm);
|
||||
pkt = (ctio_common_entry_t *)prm.pkt;
|
||||
pkt->flags = cpu_to_le16(OF_FAST_POST | OF_DATA_OUT);
|
||||
@@ -3573,6 +3597,7 @@ static int q2t_prepare_srr_ctio(scsi_qla_host_t *vha, struct q2t_cmd *cmd,
|
||||
sc, sc->srr_id);
|
||||
if (tgt->imm_srr_id == tgt->ctio_srr_id) {
|
||||
int found = 0;
|
||||
|
||||
list_for_each_entry(imm, &tgt->srr_imm_list,
|
||||
srr_list_entry) {
|
||||
if (imm->srr_id == sc->srr_id) {
|
||||
@@ -3600,6 +3625,7 @@ static int q2t_prepare_srr_ctio(scsi_qla_host_t *vha, struct q2t_cmd *cmd,
|
||||
spin_unlock(&tgt->srr_lock);
|
||||
} else {
|
||||
struct srr_imm *ti;
|
||||
|
||||
PRINT_ERROR("qla2x00t(%ld): Unable to allocate SRR CTIO entry",
|
||||
vha->host_no);
|
||||
spin_lock(&tgt->srr_lock);
|
||||
@@ -3643,6 +3669,7 @@ static bool q2t_term_ctio_exchange(scsi_qla_host_t *vha, void *ctio,
|
||||
}
|
||||
if (ctio != NULL) {
|
||||
ctio7_fw_entry_t *c = (ctio7_fw_entry_t *)ctio;
|
||||
|
||||
term = !(c->flags & cpu_to_le16(OF_TERM_EXCH));
|
||||
} else
|
||||
term = true;
|
||||
@@ -3654,6 +3681,7 @@ static bool q2t_term_ctio_exchange(scsi_qla_host_t *vha, void *ctio,
|
||||
#if 0 /* Seems, it isn't needed. If enable it, add support for NULL cmd! */
|
||||
if (ctio != NULL) {
|
||||
ctio_common_entry_t *c = (ctio_common_entry_t *)ctio;
|
||||
|
||||
term = !(c->flags & cpu_to_le16(CTIO7_FLAGS_TERMINATE));
|
||||
} else
|
||||
term = true;
|
||||
@@ -3673,6 +3701,7 @@ static inline struct q2t_cmd *q2t_get_cmd(scsi_qla_host_t *vha, uint32_t handle)
|
||||
handle--;
|
||||
if (vha->cmds[handle] != NULL) {
|
||||
struct q2t_cmd *cmd = vha->cmds[handle];
|
||||
|
||||
vha->cmds[handle] = NULL;
|
||||
return cmd;
|
||||
} else
|
||||
@@ -3728,6 +3757,7 @@ static struct q2t_cmd *q2t_ctio_to_cmd(scsi_qla_host_t *vha, uint32_t handle,
|
||||
goto out;
|
||||
} else {
|
||||
ctio_common_entry_t *c = (ctio_common_entry_t *)ctio;
|
||||
|
||||
loop_id = GET_TARGET_ID(ha, c);
|
||||
tag = c->rx_id;
|
||||
}
|
||||
@@ -4103,6 +4133,7 @@ static int q2t_send_cmd_to_scst(scsi_qla_host_t *vha, atio_t *atio)
|
||||
|
||||
if (IS_FWI2_CAPABLE(ha)) {
|
||||
atio7_entry_t *a = (atio7_entry_t *)atio;
|
||||
|
||||
sess = q2t_find_sess_by_s_id(tgt, a->fcp_hdr.s_id);
|
||||
if (unlikely(sess == NULL)) {
|
||||
TRACE_MGMT_DBG("qla2x00t(%ld): Unable to find "
|
||||
@@ -4166,6 +4197,7 @@ static int q2t_issue_task_mgmt(struct q2t_sess *sess, uint8_t *lun,
|
||||
*/
|
||||
while (!list_empty(&ha->unknown_atio_list)) {
|
||||
struct q2t_unknown_atio *u;
|
||||
|
||||
u = list_first_entry(&ha->unknown_atio_list,
|
||||
struct q2t_unknown_atio, unknown_atio_list_entry);
|
||||
TRACE_MGMT_DBG("qla2x00t(%ld): Clearing unknown "
|
||||
@@ -4300,12 +4332,14 @@ static int q2t_handle_task_mgmt(scsi_qla_host_t *vha, void *iocb)
|
||||
tgt = vha->tgt;
|
||||
if (IS_FWI2_CAPABLE(ha)) {
|
||||
atio7_entry_t *a = (atio7_entry_t *)iocb;
|
||||
|
||||
lun = (uint8_t *)&a->fcp_cmnd.lun;
|
||||
lun_size = sizeof(a->fcp_cmnd.lun);
|
||||
fn = a->fcp_cmnd.task_mgmt_flags;
|
||||
sess = q2t_find_sess_by_s_id(tgt, a->fcp_hdr.s_id);
|
||||
} else {
|
||||
notify_entry_t *n = (notify_entry_t *)iocb;
|
||||
|
||||
/* make it be in network byte order */
|
||||
lun_data = swab16(le16_to_cpu(n->lun));
|
||||
lun = (uint8_t *)&lun_data;
|
||||
@@ -4468,6 +4502,7 @@ static int q24_handle_els(scsi_qla_host_t *vha, notify24xx_entry_t *iocb)
|
||||
case ELS_ADISC:
|
||||
{
|
||||
struct q2t_tgt *tgt = vha->tgt;
|
||||
|
||||
if (tgt->link_reinit_iocb_pending) {
|
||||
q24_send_notify_ack(vha, &tgt->link_reinit_iocb, 0, 0, 0);
|
||||
tgt->link_reinit_iocb_pending = 0;
|
||||
@@ -4506,6 +4541,7 @@ static int q2t_cut_cmd_data_head(struct q2t_cmd *cmd, unsigned int offset)
|
||||
l += cmd->sg[i].length;
|
||||
if (l > offset) {
|
||||
int sg_offs = l - cmd->sg[i].length;
|
||||
|
||||
first_sg = i;
|
||||
if (cmd->sg[i].offset == 0) {
|
||||
first_page_offs = offset % PAGE_SIZE;
|
||||
@@ -4541,7 +4577,7 @@ static int q2t_cut_cmd_data_head(struct q2t_cmd *cmd, unsigned int offset)
|
||||
first_sg, first_page, first_page_offs, cmd->bufflen,
|
||||
cmd->sg_cnt);
|
||||
|
||||
sg = kmalloc(cnt * sizeof(sg[0]), GFP_KERNEL);
|
||||
sg = kmalloc_array(cnt, sizeof(sg[0]), GFP_KERNEL);
|
||||
if (sg == NULL) {
|
||||
PRINT_ERROR("qla2x00t(%ld): Unable to allocate cut "
|
||||
"SG (len %zd)", cmd->tgt->vha->host_no,
|
||||
@@ -4555,6 +4591,7 @@ static int q2t_cut_cmd_data_head(struct q2t_cmd *cmd, unsigned int offset)
|
||||
cur_src = first_sg;
|
||||
if (first_page_offs != 0) {
|
||||
int fpgs;
|
||||
|
||||
sg_set_page(&sg[cur_dst], &sg_page(&cmd->sg[cur_src])[first_page],
|
||||
PAGE_SIZE - first_page_offs, first_page_offs);
|
||||
bufflen += sg[cur_dst].length;
|
||||
@@ -4657,6 +4694,7 @@ static void q24_handle_srr(scsi_qla_host_t *vha, struct srr_ctio *sctio,
|
||||
(scst_cmd_get_data_direction(&cmd->scst_cmd) & SCST_DATA_READ)) {
|
||||
uint32_t offset;
|
||||
int xmit_type;
|
||||
|
||||
offset = le32_to_cpu(imm->imm.notify_entry24.srr_rel_offs);
|
||||
if (q2t_srr_adjust_data(cmd, offset, &xmit_type) != 0)
|
||||
goto out_reject;
|
||||
@@ -4681,6 +4719,7 @@ static void q24_handle_srr(scsi_qla_host_t *vha, struct srr_ctio *sctio,
|
||||
(scst_cmd_get_data_direction(&cmd->scst_cmd) & SCST_DATA_WRITE)) {
|
||||
uint32_t offset;
|
||||
int xmit_type;
|
||||
|
||||
offset = le32_to_cpu(imm->imm.notify_entry24.srr_rel_offs);
|
||||
if (q2t_srr_adjust_data(cmd, offset, &xmit_type) != 0)
|
||||
goto out_reject;
|
||||
@@ -4751,6 +4790,7 @@ static void q2x_handle_srr(scsi_qla_host_t *vha, struct srr_ctio *sctio,
|
||||
if (q2t_has_data(cmd)) {
|
||||
uint32_t offset;
|
||||
int xmit_type;
|
||||
|
||||
offset = le32_to_cpu(imm->imm.notify_entry.srr_rel_offs);
|
||||
if (q2t_srr_adjust_data(cmd, offset, &xmit_type) != 0)
|
||||
goto out_reject;
|
||||
@@ -4773,6 +4813,7 @@ static void q2x_handle_srr(scsi_qla_host_t *vha, struct srr_ctio *sctio,
|
||||
if (q2t_has_data(cmd)) {
|
||||
uint32_t offset;
|
||||
int xmit_type;
|
||||
|
||||
offset = le32_to_cpu(imm->imm.notify_entry.srr_rel_offs);
|
||||
if (q2t_srr_adjust_data(cmd, offset, &xmit_type) != 0)
|
||||
goto out_reject;
|
||||
@@ -4949,6 +4990,7 @@ static void q2t_prepare_srr_imm(scsi_qla_host_t *vha, void *iocb)
|
||||
imm->srr_id, iocb24->srr_ui);
|
||||
if (tgt->imm_srr_id == tgt->ctio_srr_id) {
|
||||
int found = 0;
|
||||
|
||||
list_for_each_entry(sctio, &tgt->srr_ctio_list,
|
||||
srr_list_entry) {
|
||||
if (sctio->srr_id == imm->srr_id) {
|
||||
@@ -5064,6 +5106,7 @@ static void q2t_handle_imm_notify(scsi_qla_host_t *vha, void *iocb)
|
||||
case IMM_NTFY_LIP_LINK_REINIT:
|
||||
{
|
||||
struct q2t_tgt *tgt = vha->tgt;
|
||||
|
||||
TRACE(TRACE_MGMT, "qla2x00t(%ld): LINK REINIT (loop %#x, "
|
||||
"subcode %x)", vha->host_no,
|
||||
le16_to_cpu(iocb24->nport_handle),
|
||||
@@ -5359,6 +5402,7 @@ static void q24_atio_pkt(scsi_qla_host_t *vha, atio7_entry_t *atio)
|
||||
case IMMED_NOTIFY_TYPE:
|
||||
{
|
||||
notify_entry_t *pkt = (notify_entry_t *)atio;
|
||||
|
||||
if (unlikely(pkt->entry_status != 0)) {
|
||||
PRINT_ERROR("qla2x00t(%ld): Received ATIO packet %x "
|
||||
"with error status %x", vha->host_no,
|
||||
@@ -5426,6 +5470,7 @@ static void q2t_response_pkt(scsi_qla_host_t *vha, response_t *pkt)
|
||||
case CTIO_TYPE7:
|
||||
{
|
||||
ctio7_fw_entry_t *entry = (ctio7_fw_entry_t *)pkt;
|
||||
|
||||
TRACE_DBG("CTIO_TYPE7: instance %ld",
|
||||
vha->host_no);
|
||||
TRACE_BUFFER("Incoming CTIO7 packet data", entry,
|
||||
@@ -5440,6 +5485,7 @@ static void q2t_response_pkt(scsi_qla_host_t *vha, response_t *pkt)
|
||||
{
|
||||
atio_entry_t *atio;
|
||||
int rc;
|
||||
|
||||
atio = (atio_entry_t *)pkt;
|
||||
TRACE_DBG("ACCEPT_TGT_IO instance %ld status %04x "
|
||||
"lun %04x read/write %d data_length %04x "
|
||||
@@ -5481,6 +5527,7 @@ static void q2t_response_pkt(scsi_qla_host_t *vha, response_t *pkt)
|
||||
case CONTINUE_TGT_IO_TYPE:
|
||||
{
|
||||
ctio_common_entry_t *entry = (ctio_common_entry_t *)pkt;
|
||||
|
||||
TRACE_DBG("CONTINUE_TGT_IO: instance %ld", vha->host_no);
|
||||
TRACE_BUFFER("Incoming CTIO packet data", entry,
|
||||
REQUEST_ENTRY_SIZE);
|
||||
@@ -5493,6 +5540,7 @@ static void q2t_response_pkt(scsi_qla_host_t *vha, response_t *pkt)
|
||||
case CTIO_A64_TYPE:
|
||||
{
|
||||
ctio_common_entry_t *entry = (ctio_common_entry_t *)pkt;
|
||||
|
||||
TRACE_DBG("CTIO_A64: instance %ld", vha->host_no);
|
||||
TRACE_BUFFER("Incoming CTIO_A64 packet data", entry,
|
||||
REQUEST_ENTRY_SIZE);
|
||||
@@ -5510,6 +5558,7 @@ static void q2t_response_pkt(scsi_qla_host_t *vha, response_t *pkt)
|
||||
case NOTIFY_ACK_TYPE:
|
||||
if (tgt->notify_ack_expected > 0) {
|
||||
nack_entry_t *entry = (nack_entry_t *)pkt;
|
||||
|
||||
TRACE_DBG("NOTIFY_ACK seq %08x status %x",
|
||||
le16_to_cpu(entry->seq_id),
|
||||
le16_to_cpu(entry->status));
|
||||
@@ -5538,6 +5587,7 @@ static void q2t_response_pkt(scsi_qla_host_t *vha, response_t *pkt)
|
||||
if (tgt->abts_resp_expected > 0) {
|
||||
abts24_resp_fw_entry_t *entry =
|
||||
(abts24_resp_fw_entry_t *)pkt;
|
||||
|
||||
TRACE_DBG("ABTS_RESP_24XX: compl_status %x",
|
||||
entry->compl_status);
|
||||
TRACE_BUFF_FLAG(TRACE_BUFF, "Incoming ABTS_RESP "
|
||||
@@ -5574,6 +5624,7 @@ static void q2t_response_pkt(scsi_qla_host_t *vha, response_t *pkt)
|
||||
case MODIFY_LUN_TYPE:
|
||||
if (tgt->modify_lun_expected > 0) {
|
||||
modify_lun_entry_t *entry = (modify_lun_entry_t *)pkt;
|
||||
|
||||
TRACE_DBG("MODIFY_LUN %x, imm %c%d, cmd %c%d",
|
||||
entry->status,
|
||||
(entry->operators & MODIFY_LUN_IMM_ADD) ? '+'
|
||||
@@ -5599,6 +5650,7 @@ static void q2t_response_pkt(scsi_qla_host_t *vha, response_t *pkt)
|
||||
case ENABLE_LUN_TYPE:
|
||||
{
|
||||
elun_entry_t *entry = (elun_entry_t *)pkt;
|
||||
|
||||
TRACE_DBG("ENABLE_LUN %x imm %u cmd %u ",
|
||||
entry->status, entry->immed_notify_count,
|
||||
entry->command_count);
|
||||
@@ -5835,8 +5887,10 @@ static void q2t_exec_sess_work(struct q2t_tgt *tgt,
|
||||
case Q2T_SESS_WORK_CMD:
|
||||
{
|
||||
struct q2t_cmd *cmd = prm->cmd;
|
||||
|
||||
if (IS_FWI2_CAPABLE(ha)) {
|
||||
atio7_entry_t *a = (atio7_entry_t *)&cmd->atio;
|
||||
|
||||
s_id = a->fcp_hdr.s_id;
|
||||
} else
|
||||
loop_id = GET_TARGET_ID(ha, (atio_entry_t *)&cmd->atio);
|
||||
@@ -5896,6 +5950,7 @@ send:
|
||||
case Q2T_SESS_WORK_CMD:
|
||||
{
|
||||
struct q2t_cmd *cmd = prm->cmd;
|
||||
|
||||
if (tgt->tm_to_unknown) {
|
||||
/*
|
||||
* Cmd might be already aborted behind us, so be safe
|
||||
@@ -5923,12 +5978,14 @@ send:
|
||||
|
||||
if (IS_FWI2_CAPABLE(ha)) {
|
||||
atio7_entry_t *a = &prm->tm_iocb2;
|
||||
|
||||
iocb = a;
|
||||
lun = (uint8_t *)&a->fcp_cmnd.lun;
|
||||
lun_size = sizeof(a->fcp_cmnd.lun);
|
||||
fn = a->fcp_cmnd.task_mgmt_flags;
|
||||
} else {
|
||||
notify_entry_t *n = &prm->tm_iocb;
|
||||
|
||||
iocb = n;
|
||||
/* make it be in network byte order */
|
||||
lun_data = swab16(le16_to_cpu(n->lun));
|
||||
@@ -5962,6 +6019,7 @@ out_term:
|
||||
case Q2T_SESS_WORK_CMD:
|
||||
{
|
||||
struct q2t_cmd *cmd = prm->cmd;
|
||||
|
||||
TRACE_MGMT_DBG("Terminating work cmd %p", cmd);
|
||||
/*
|
||||
* cmd has not sent to SCST yet, so pass NULL as the second
|
||||
@@ -6195,8 +6253,10 @@ static int q2t_add_target(scsi_qla_host_t *vha)
|
||||
scst_get_tgt_name(tgt->scst_tgt));
|
||||
if (vha->vp_idx == 0) {
|
||||
int i = 0;
|
||||
|
||||
while (1) {
|
||||
const struct attribute *a = q2t_hw_tgt_attrs[i];
|
||||
|
||||
if (a == NULL)
|
||||
break;
|
||||
rc = sysfs_create_file(scst_sysfs_get_tgt_kobj(tgt->scst_tgt), a);
|
||||
@@ -6209,8 +6269,10 @@ static int q2t_add_target(scsi_qla_host_t *vha)
|
||||
}
|
||||
} else {
|
||||
int i = 0;
|
||||
|
||||
while (1) {
|
||||
const struct attribute *a = q2t_npiv_tgt_attrs[i];
|
||||
|
||||
if (a == NULL)
|
||||
break;
|
||||
rc = sysfs_create_file(scst_sysfs_get_tgt_kobj(tgt->scst_tgt), a);
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
/* Version numbers, the same as for the kernel */
|
||||
#define Q2T_VERSION(a, b, c, d) (((a) << 030) + ((b) << 020) + (c) << 010 + (d))
|
||||
#define Q2T_VERSION_CODE Q2T_VERSION(4, 1, 0, 0)
|
||||
#define Q2T_VERSION_STRING "3.1.0-pre1"
|
||||
#define Q2T_VERSION_STRING "3.2.0-pre1"
|
||||
#define Q2T_PROC_VERSION_NAME "version"
|
||||
|
||||
#define Q2T_MAX_CDB_LEN 16
|
||||
|
||||
@@ -124,6 +124,7 @@ qla2x00_send_enable_lun(scsi_qla_host_t *vha, bool enable)
|
||||
|
||||
if (!IS_FWI2_CAPABLE(ha)) {
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ha->hardware_lock, flags);
|
||||
__qla2x00_send_enable_lun(vha, enable);
|
||||
spin_unlock_irqrestore(&ha->hardware_lock, flags);
|
||||
@@ -159,6 +160,7 @@ static inline bool qla_firmware_active(scsi_qla_host_t *vha)
|
||||
{
|
||||
struct qla_hw_data *ha = vha->hw;
|
||||
struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
|
||||
|
||||
return qla_tgt_mode_enabled(base_vha) || qla_ini_mode_enabled(base_vha);
|
||||
}
|
||||
|
||||
|
||||
+25
-25
@@ -148,7 +148,7 @@ typedef struct {
|
||||
uint16_t reserved_5;
|
||||
uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */
|
||||
uint16_t reserved_6[20];
|
||||
} __attribute__((packed)) elun_entry_t;
|
||||
} __packed elun_entry_t;
|
||||
#define ENABLE_LUN_SUCCESS 0x01
|
||||
#define ENABLE_LUN_RC_NONZERO 0x04
|
||||
#define ENABLE_LUN_INVALID_REQUEST 0x06
|
||||
@@ -179,7 +179,7 @@ typedef struct {
|
||||
uint16_t reserved_5;
|
||||
uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */
|
||||
uint16_t reserved_7[20];
|
||||
} __attribute__((packed)) modify_lun_entry_t;
|
||||
} __packed modify_lun_entry_t;
|
||||
#define MODIFY_LUN_SUCCESS 0x01
|
||||
#define MODIFY_LUN_CMD_ADD BIT_0
|
||||
#define MODIFY_LUN_CMD_SUB BIT_1
|
||||
@@ -219,7 +219,7 @@ typedef struct {
|
||||
uint16_t srr_ox_id;
|
||||
uint8_t reserved_2[30];
|
||||
uint16_t ox_id;
|
||||
} __attribute__((packed)) notify_entry_t;
|
||||
} __packed notify_entry_t;
|
||||
#endif
|
||||
|
||||
#ifndef NOTIFY_ACK_TYPE
|
||||
@@ -250,7 +250,7 @@ typedef struct {
|
||||
uint8_t srr_reject_code_expl;
|
||||
uint8_t reserved_2[26];
|
||||
uint16_t ox_id;
|
||||
} __attribute__((packed)) nack_entry_t;
|
||||
} __packed nack_entry_t;
|
||||
#define NOTIFY_ACK_SRR_FLAGS_ACCEPT 0
|
||||
#define NOTIFY_ACK_SRR_FLAGS_REJECT 1
|
||||
|
||||
@@ -287,7 +287,7 @@ typedef struct {
|
||||
uint8_t initiator_port_name[WWN_SIZE]; /* on qla23xx */
|
||||
uint16_t reserved_32[6];
|
||||
uint16_t ox_id;
|
||||
} __attribute__((packed)) atio_entry_t;
|
||||
} __packed atio_entry_t;
|
||||
#endif
|
||||
|
||||
#ifndef CONTINUE_TGT_IO_TYPE
|
||||
@@ -314,7 +314,7 @@ typedef struct {
|
||||
uint16_t scsi_status;
|
||||
uint32_t transfer_length;
|
||||
uint32_t dseg_0_address[0];
|
||||
} __attribute__((packed)) ctio_common_entry_t;
|
||||
} __packed ctio_common_entry_t;
|
||||
#define ATIO_PATH_INVALID 0x07
|
||||
#define ATIO_CANT_PROV_CAP 0x16
|
||||
#define ATIO_CDB_VALID 0x3D
|
||||
@@ -333,7 +333,7 @@ typedef struct {
|
||||
uint32_t dseg_1_length; /* Data segment 1 length. */
|
||||
uint32_t dseg_2_address; /* Data segment 2 address. */
|
||||
uint32_t dseg_2_length; /* Data segment 2 length. */
|
||||
} __attribute__((packed)) ctio_entry_t;
|
||||
} __packed ctio_entry_t;
|
||||
#define CTIO_SUCCESS 0x01
|
||||
#define CTIO_ABORTED 0x02
|
||||
#define CTIO_INVALID_RX_ID 0x08
|
||||
@@ -371,7 +371,7 @@ typedef struct {
|
||||
uint16_t scsi_status;
|
||||
uint16_t response_length;
|
||||
uint8_t sense_data[26];
|
||||
} __attribute__((packed)) ctio_ret_entry_t;
|
||||
} __packed ctio_ret_entry_t;
|
||||
#endif
|
||||
|
||||
#define ATIO_TYPE7 0x06 /* Accept target I/O entry for 24xx */
|
||||
@@ -389,7 +389,7 @@ typedef struct {
|
||||
uint16_t ox_id;
|
||||
uint16_t rx_id;
|
||||
uint32_t parameter;
|
||||
} __attribute__((packed)) fcp_hdr_t;
|
||||
} __packed fcp_hdr_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t d_id[3];
|
||||
@@ -404,7 +404,7 @@ typedef struct {
|
||||
uint16_t rx_id;
|
||||
uint16_t ox_id;
|
||||
uint32_t parameter;
|
||||
} __attribute__((packed)) fcp_hdr_le_t;
|
||||
} __packed fcp_hdr_le_t;
|
||||
|
||||
#define F_CTL_EXCH_CONTEXT_RESP BIT_23
|
||||
#define F_CTL_SEQ_CONTEXT_RESIP BIT_22
|
||||
@@ -449,7 +449,7 @@ typedef struct {
|
||||
*/
|
||||
uint8_t add_cdb[4];
|
||||
/* uint32_t data_length; */
|
||||
} __attribute__((packed)) fcp_cmnd_t;
|
||||
} __packed fcp_cmnd_t;
|
||||
|
||||
/*
|
||||
* ISP queue - Accept Target I/O (ATIO) type 7 entry for 24xx structure
|
||||
@@ -470,7 +470,7 @@ typedef struct {
|
||||
#define ATIO_EXCHANGE_ADDRESS_UNKNOWN 0xFFFFFFFF
|
||||
fcp_hdr_t fcp_hdr;
|
||||
fcp_cmnd_t fcp_cmnd;
|
||||
} __attribute__((packed)) atio7_entry_t;
|
||||
} __packed atio7_entry_t;
|
||||
|
||||
#define CTIO_TYPE7 0x12 /* Continue target I/O entry (for 24xx) */
|
||||
|
||||
@@ -494,7 +494,7 @@ typedef struct {
|
||||
uint8_t initiator_id[3];
|
||||
uint8_t reserved;
|
||||
uint32_t exchange_addr;
|
||||
} __attribute__((packed)) ctio7_common_entry_t;
|
||||
} __packed ctio7_common_entry_t;
|
||||
|
||||
typedef struct {
|
||||
ctio7_common_entry_t common;
|
||||
@@ -509,7 +509,7 @@ typedef struct {
|
||||
uint32_t reserved3;
|
||||
uint32_t dseg_0_address[2]; /* Data segment 0 address. */
|
||||
uint32_t dseg_0_length; /* Data segment 0 length. */
|
||||
} __attribute__((packed)) ctio7_status0_entry_t;
|
||||
} __packed ctio7_status0_entry_t;
|
||||
|
||||
typedef struct {
|
||||
ctio7_common_entry_t common;
|
||||
@@ -521,7 +521,7 @@ typedef struct {
|
||||
uint16_t response_len;
|
||||
uint16_t reserved;
|
||||
uint8_t sense_data[24];
|
||||
} __attribute__((packed)) ctio7_status1_entry_t;
|
||||
} __packed ctio7_status1_entry_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t entry_type; /* Entry type. */
|
||||
@@ -542,7 +542,7 @@ typedef struct {
|
||||
uint16_t reserved3;
|
||||
uint32_t relative_offset;
|
||||
uint8_t reserved4[24];
|
||||
} __attribute__((packed)) ctio7_fw_entry_t;
|
||||
} __packed ctio7_fw_entry_t;
|
||||
|
||||
/* CTIO7 flags values */
|
||||
#define CTIO7_FLAGS_SEND_STATUS BIT_15
|
||||
@@ -589,7 +589,7 @@ typedef struct {
|
||||
uint8_t reserved_7;
|
||||
uint16_t reserved_8;
|
||||
uint16_t ox_id;
|
||||
} __attribute__((packed)) notify24xx_entry_t;
|
||||
} __packed notify24xx_entry_t;
|
||||
|
||||
#define ELS_PLOGI 0x3
|
||||
#define ELS_FLOGI 0x4
|
||||
@@ -627,13 +627,13 @@ typedef struct {
|
||||
uint8_t srr_reject_code;
|
||||
uint8_t reserved_5[7];
|
||||
uint16_t ox_id;
|
||||
} __attribute__((packed)) nack24xx_entry_t;
|
||||
} __packed nack24xx_entry_t;
|
||||
|
||||
/*
|
||||
* ISP queue - ABTS received/response entries structure definition for 24xx.
|
||||
*/
|
||||
#define ABTS_RECV_24XX 0x54 /* ABTS received (for 24xx) */
|
||||
#define ABTS_RESP_24XX 0x55 /* ABTS responce (for 24xx) */
|
||||
#define ABTS_RESP_24XX 0x55 /* ABTS response (for 24xx) */
|
||||
|
||||
typedef struct {
|
||||
uint8_t entry_type; /* Entry type. */
|
||||
@@ -655,7 +655,7 @@ typedef struct {
|
||||
fcp_hdr_le_t fcp_hdr_le;
|
||||
uint8_t reserved_4[16];
|
||||
uint32_t exchange_addr_to_abort;
|
||||
} __attribute__((packed)) abts24_recv_entry_t;
|
||||
} __packed abts24_recv_entry_t;
|
||||
|
||||
#define ABTS_PARAM_ABORT_SEQ BIT_0
|
||||
|
||||
@@ -669,7 +669,7 @@ typedef struct {
|
||||
uint16_t ox_id;
|
||||
uint16_t high_seq_cnt;
|
||||
uint16_t low_seq_cnt;
|
||||
} __attribute__((packed)) ba_acc_le_t;
|
||||
} __packed ba_acc_le_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t vendor_uniq;
|
||||
@@ -678,7 +678,7 @@ typedef struct {
|
||||
#define BA_RJT_REASON_CODE_INVALID_COMMAND 0x1
|
||||
#define BA_RJT_REASON_CODE_UNABLE_TO_PERFORM 0x9
|
||||
uint8_t reserved;
|
||||
} __attribute__((packed)) ba_rjt_le_t;
|
||||
} __packed ba_rjt_le_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t entry_type; /* Entry type. */
|
||||
@@ -703,10 +703,10 @@ typedef struct {
|
||||
union {
|
||||
ba_acc_le_t ba_acct;
|
||||
ba_rjt_le_t ba_rjt;
|
||||
} __attribute__((packed)) payload;
|
||||
} __packed payload;
|
||||
uint32_t reserved_4;
|
||||
uint32_t exchange_addr_to_abort;
|
||||
} __attribute__((packed)) abts24_resp_entry_t;
|
||||
} __packed abts24_resp_entry_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t entry_type; /* Entry type. */
|
||||
@@ -734,7 +734,7 @@ typedef struct {
|
||||
#define ABTS_RESP_SUBCODE_ERR_ABORTED_EXCH_NOT_TERM 0x1E
|
||||
uint32_t error_subcode2;
|
||||
uint32_t exchange_addr_to_abort;
|
||||
} __attribute__((packed)) abts24_resp_fw_entry_t;
|
||||
} __packed abts24_resp_fw_entry_t;
|
||||
|
||||
/********************************************************************\
|
||||
* Type Definitions used by initiator & target halves
|
||||
|
||||
+4
-4
@@ -126,12 +126,12 @@ struct qla84_msg_mgmt {
|
||||
|
||||
struct qla_bsg_a84_mgmt {
|
||||
struct qla84_msg_mgmt mgmt;
|
||||
} __attribute__ ((packed));
|
||||
} __packed;
|
||||
|
||||
struct qla_scsi_addr {
|
||||
uint16_t bus;
|
||||
uint16_t target;
|
||||
} __attribute__ ((packed));
|
||||
} __packed;
|
||||
|
||||
struct qla_ext_dest_addr {
|
||||
union {
|
||||
@@ -144,13 +144,13 @@ struct qla_ext_dest_addr {
|
||||
#define EXT_DEF_TYPE_WWPN 2
|
||||
uint16_t lun;
|
||||
uint16_t padding[2];
|
||||
} __attribute__ ((packed));
|
||||
} __packed;
|
||||
|
||||
struct qla_port_param {
|
||||
struct qla_ext_dest_addr fc_scsi_addr;
|
||||
uint16_t mode;
|
||||
uint16_t speed;
|
||||
} __attribute__ ((packed));
|
||||
} __packed;
|
||||
|
||||
|
||||
/* FRU VPD */
|
||||
|
||||
+3
-3
@@ -361,7 +361,7 @@ struct device_reg_2xxx {
|
||||
uint16_t mailbox6;
|
||||
uint16_t mailbox7;
|
||||
uint16_t unused_2[59]; /* Gap */
|
||||
} __attribute__((packed)) isp2100;
|
||||
} __packed isp2100;
|
||||
struct {
|
||||
/* Request Queue */
|
||||
uint16_t req_q_in; /* In-Pointer */
|
||||
@@ -412,7 +412,7 @@ struct device_reg_2xxx {
|
||||
uint16_t mailbox31;
|
||||
uint16_t fb_cmd;
|
||||
uint16_t unused_4[10]; /* Gap */
|
||||
} __attribute__((packed)) isp2300;
|
||||
} __packed isp2300;
|
||||
} u;
|
||||
|
||||
uint16_t fpm_diag_config;
|
||||
@@ -469,7 +469,7 @@ struct device_reg_2xxx {
|
||||
uint16_t mailbox21;
|
||||
uint16_t mailbox22;
|
||||
uint16_t mailbox23; /* Also probe reg. */
|
||||
} __attribute__((packed)) isp2200;
|
||||
} __packed isp2200;
|
||||
} u_end;
|
||||
};
|
||||
|
||||
|
||||
+4
-4
@@ -2632,8 +2632,8 @@ qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp)
|
||||
struct qla_msix_entry *qentry;
|
||||
scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
|
||||
|
||||
entries = kzalloc(sizeof(struct msix_entry) * ha->msix_count,
|
||||
GFP_KERNEL);
|
||||
entries = kcalloc(ha->msix_count, sizeof(struct msix_entry),
|
||||
GFP_KERNEL);
|
||||
if (!entries) {
|
||||
ql_log(ql_log_warn, vha, 0x00bc,
|
||||
"Failed to allocate memory for msix_entry.\n");
|
||||
@@ -2664,8 +2664,8 @@ msix_failed:
|
||||
}
|
||||
ha->max_rsp_queues = ha->msix_count - 1;
|
||||
}
|
||||
ha->msix_entries = kzalloc(sizeof(struct qla_msix_entry) *
|
||||
ha->msix_count, GFP_KERNEL);
|
||||
ha->msix_entries = kcalloc(ha->msix_count,
|
||||
sizeof(struct qla_msix_entry), GFP_KERNEL);
|
||||
if (!ha->msix_entries) {
|
||||
ql_log(ql_log_fatal, vha, 0x00c8,
|
||||
"Failed to allocate memory for ha->msix_entries.\n");
|
||||
|
||||
+1
-1
@@ -1222,7 +1222,7 @@ qla82xx_pinit_from_rom(scsi_qla_host_t *vha)
|
||||
ql_log(ql_log_info, vha, 0x0072,
|
||||
"%d CRB init values found in ROM.\n", n);
|
||||
|
||||
buf = kmalloc(n * sizeof(struct crb_addr_pair), GFP_KERNEL);
|
||||
buf = kmalloc_array(n, sizeof(struct crb_addr_pair), GFP_KERNEL);
|
||||
if (buf == NULL) {
|
||||
ql_log(ql_log_fatal, vha, 0x010c,
|
||||
"Unable to allocate memory.\n");
|
||||
|
||||
+8
-6
@@ -367,7 +367,7 @@ static int qla2x00_alloc_queues(struct qla_hw_data *ha, struct req_que *req,
|
||||
struct rsp_que *rsp)
|
||||
{
|
||||
scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
|
||||
ha->req_q_map = kzalloc(sizeof(struct req_que *) * ha->max_req_queues,
|
||||
ha->req_q_map = kcalloc(ha->max_req_queues, sizeof(struct req_que *),
|
||||
GFP_KERNEL);
|
||||
if (!ha->req_q_map) {
|
||||
ql_log(ql_log_fatal, vha, 0x003b,
|
||||
@@ -375,7 +375,7 @@ static int qla2x00_alloc_queues(struct qla_hw_data *ha, struct req_que *req,
|
||||
goto fail_req_map;
|
||||
}
|
||||
|
||||
ha->rsp_q_map = kzalloc(sizeof(struct rsp_que *) * ha->max_rsp_queues,
|
||||
ha->rsp_q_map = kcalloc(ha->max_rsp_queues, sizeof(struct rsp_que *),
|
||||
GFP_KERNEL);
|
||||
if (!ha->rsp_q_map) {
|
||||
ql_log(ql_log_fatal, vha, 0x003c,
|
||||
@@ -3286,8 +3286,9 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
|
||||
|
||||
#ifdef CONFIG_SCSI_QLA2XXX_TARGET
|
||||
if (IS_FWI2_CAPABLE(ha)) {
|
||||
ha->tgt_vp_map = kzalloc(sizeof(struct qla_tgt_vp_map) *
|
||||
MAX_MULTI_ID_FABRIC, GFP_KERNEL);
|
||||
ha->tgt_vp_map = kcalloc(MAX_MULTI_ID_FABRIC,
|
||||
sizeof(struct qla_tgt_vp_map),
|
||||
GFP_KERNEL);
|
||||
if (!ha->tgt_vp_map)
|
||||
goto fail_free_init_cb;
|
||||
|
||||
@@ -3433,8 +3434,9 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
|
||||
(*rsp)->ring);
|
||||
/* Allocate memory for NVRAM data for vports */
|
||||
if (ha->nvram_npiv_size) {
|
||||
ha->npiv_info = kzalloc(sizeof(struct qla_npiv_entry) *
|
||||
ha->nvram_npiv_size, GFP_KERNEL);
|
||||
ha->npiv_info = kcalloc(ha->nvram_npiv_size,
|
||||
sizeof(struct qla_npiv_entry),
|
||||
GFP_KERNEL);
|
||||
if (!ha->npiv_info) {
|
||||
ql_log_pci(ql_log_fatal, ha->pdev, 0x002d,
|
||||
"Failed to allocate memory for npiv_info.\n");
|
||||
|
||||
@@ -302,7 +302,8 @@ do
|
||||
done
|
||||
|
||||
|
||||
scst_03_public_headers="scst/include/scst.h scst/include/scst_const.h scst/include/backport.h"
|
||||
scst_03_public_headers="scst/include/scst.h scst/include/scst_const.h \
|
||||
scst/include/scst_event.h scst/include/backport.h"
|
||||
scst_04_main="scst/src/scst_main.c scst/src/scst_module.c scst/src/scst_priv.h"
|
||||
scst_05_targ="scst/src/scst_targ.c"
|
||||
scst_06_lib="scst/src/scst_lib.c"
|
||||
|
||||
+2
-1
@@ -8,7 +8,8 @@
|
||||
# version number when building on a koji build server.
|
||||
# - Otherwise use the version number of the running kernel.
|
||||
%{!?kversion:
|
||||
%{?kdir:%define kversion %{expand:%%(make -sC %{kdir} kernelversion)}}
|
||||
%{?kdir:%define kversion %{expand:%%(
|
||||
make -sC "%{kdir}" kernelversion | grep -v ^make)}}
|
||||
%{!?kdir:
|
||||
%define kversion %{expand:%%(
|
||||
if rpm --quiet -q kernel-headers; then
|
||||
|
||||
+233
-30
@@ -1,7 +1,7 @@
|
||||
Generic SCSI target mid-level for Linux (SCST)
|
||||
==============================================
|
||||
|
||||
Version 3.1.0, XX XXXXX 2014
|
||||
Version 3.2.0, XX XXXXX 2015
|
||||
----------------------------
|
||||
|
||||
SCST is designed to provide unified, consistent interface between SCSI
|
||||
@@ -508,6 +508,8 @@ documentation for your dev handlers for more info about it as well as
|
||||
SysfsRules file for more info about common to all dev handlers rules.
|
||||
SCST dev handlers can have the following common entries:
|
||||
|
||||
- block - allows to temporary block and unblock this device. See below.
|
||||
|
||||
- exported - subdirectory containing links to all LUNs where this
|
||||
device was exported.
|
||||
|
||||
@@ -531,6 +533,35 @@ SCST dev handlers can have the following common entries:
|
||||
|
||||
- type - SCSI type of this device
|
||||
|
||||
Attribute "block" allows to temporary block and unblock this device.
|
||||
"Blocking" means that no new commands for this device will go into the
|
||||
execution stage, but instead will be suspended just before it. The
|
||||
blocked state is not reached until queue of the corresponding device is
|
||||
completely drained. You can also call this state "frozen". It is useful
|
||||
in many cases, like consistent snapshots and graceful shutdown.
|
||||
|
||||
On write "block" entry allows the following 3 types of parameters:
|
||||
|
||||
- 1 - block device synchronously, i.e. don't return until this device
|
||||
becomes blocked, i.e. until queue of it is not completely drained. Can
|
||||
be called as many times as needed.
|
||||
|
||||
- 11 params - block device asynchronously, i.e. return immediately.
|
||||
Notification about completing is delivered using SCST_EVENT_EXT_BLOCKING_DONE
|
||||
event. "Params" delivered to it as is in "data" payload. Can be
|
||||
called as many times as needed. Alternatively, status of blocking could be
|
||||
polled by reading this attributes until the second number reaches 0
|
||||
(see below).
|
||||
|
||||
- 0 - unblock this device.
|
||||
|
||||
Reading from "block" entry returns two numbers separated by space:
|
||||
|
||||
1. How many times this device was blocked, i.e. how many times writing
|
||||
"0" to it is needed to unblock this device.
|
||||
|
||||
2. Boolean (0 or 1) if blocking, if any, is done (0) or still pending (1).
|
||||
|
||||
See below for more information about other entries of this subdirectory
|
||||
of the standard SCST dev handlers.
|
||||
|
||||
@@ -1257,6 +1288,7 @@ Each vdisk_fileio's device has the following attributes in
|
||||
For example:
|
||||
|
||||
/sys/kernel/scst_tgt/devices/disk1
|
||||
|-- block
|
||||
|-- blocksize
|
||||
|-- exported
|
||||
| |-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/0
|
||||
@@ -1420,16 +1452,18 @@ persistent reservations from this device are released, upon reconnect
|
||||
the initiators will see it.
|
||||
|
||||
|
||||
Implicit ALUA Support
|
||||
---------------------
|
||||
ALUA Support
|
||||
------------
|
||||
|
||||
SCST supports implicit asymmetric logical unit access (ALUA). Implicit ALUA is
|
||||
a feature defined by the ANSI T10 SCSI committee that allows a target to tell
|
||||
the initiator which path to use in a multipath setup. The redundant paths
|
||||
between initiator and target can be used either for redundancy or for load
|
||||
sharing purposes. The target can either be a single target system running SCST
|
||||
with multiple communication interfaces or two target systems each running SCST
|
||||
and configured in a high availability setup.
|
||||
SCST supports both implicit and explicit asymmetric logical unit access
|
||||
(ALUA). ALUA is a feature defined by the ANSI T10 SCSI committee. It
|
||||
allows a target to tell the initiator which path to use in a multipath
|
||||
setup plus, in the explicit case, control state of each path via SET
|
||||
TARGET PORT GROUPS SCSI command. The redundant paths between initiator
|
||||
and target can be used either for redundancy or for load sharing
|
||||
purposes. The target can either be a single target system running SCST
|
||||
with multiple communication interfaces or two target systems each
|
||||
running SCST and configured in a high availability setup.
|
||||
|
||||
In the SPC-4 standard the following concepts are defined related to ALUA:
|
||||
* Relative target port ID. A number between 1 and 65535 that uniquely
|
||||
@@ -1450,17 +1484,17 @@ ANSI T10 standard called SPC-4.
|
||||
ALUA support in SCST
|
||||
....................
|
||||
|
||||
SCST allows to define implicit ALUA settings for each unique combination of
|
||||
SCST device and SCST target. An initiator however queries ALUA settings by
|
||||
sending an appropriate SCSI command to a specific LUN of an SCST target. Each
|
||||
such LUN maps uniquely to an SCST device. For hardware SCST target drivers,
|
||||
e.g. ib_srpt, there is a one-to-one correspondence between SCST target and
|
||||
SCSI target port. With other SCST targets, e.g. iSCSI-SCST, by default the
|
||||
only relationship between SCST targets and SCSI target ports is that all SCST
|
||||
targets defined on a system are visible via all SCSI target ports. See also
|
||||
the iSCSI-SCST documentation about the allowed_portal attribute for
|
||||
information about how to associate iSCSI targets with a single physical
|
||||
interface.
|
||||
SCST allows to define ALUA settings for each unique combination of SCST
|
||||
device and SCST target. An initiator however queries ALUA settings by
|
||||
sending an appropriate SCSI command to a specific LUN of an SCST target.
|
||||
Each such LUN maps uniquely to an SCST device. For hardware SCST target
|
||||
drivers, e.g. ib_srpt, there is a one-to-one correspondence between SCST
|
||||
target and SCSI target port. With other SCST targets, e.g. iSCSI-SCST,
|
||||
by default the only relationship between SCST targets and SCSI target
|
||||
ports is that all SCST targets defined on a system are visible via all
|
||||
SCSI target ports. See also the iSCSI-SCST documentation about the
|
||||
allowed_portal attribute for information about how to associate iSCSI
|
||||
targets with a single physical interface.
|
||||
|
||||
Notes:
|
||||
- In a H.A. setup it is the responsibility of the user to synchronize ALUA
|
||||
@@ -1483,7 +1517,7 @@ Notes:
|
||||
Configuring ALUA in SCST
|
||||
........................
|
||||
|
||||
SCST allows to configure the following settings related to implicit ALUA
|
||||
SCST allows to configure the following settings related to ALUA
|
||||
for each unique combination of SCST target and virtual SCST device
|
||||
(vdisk_fileio, vdisk_blockio, vcdrom, ...):
|
||||
* The target port group asymmetric access state. SCST supports all ALUA port
|
||||
@@ -1581,15 +1615,62 @@ DEVICE_GROUP dgroup2 {
|
||||
}
|
||||
}
|
||||
|
||||
Explicit ALUA
|
||||
.............
|
||||
|
||||
To enable explicit ALUA you need in addition to the above settings set
|
||||
expl_alua device attribute to 1 (by default it is 0). Also you need to
|
||||
run stpgd and supply to it path to a script or program, which will
|
||||
perform actual path state switching on SET TARGET PORT GROUPS command,
|
||||
for instance, by calling drbdadm. For more information see stpgd README
|
||||
as well as sample script scst_on_stpg.
|
||||
|
||||
DRBD compatibility
|
||||
..................
|
||||
|
||||
DRBD does not allow to open its device on the secondary as well as does
|
||||
not allow to perform primary to secondary transition, if this device is
|
||||
open.
|
||||
|
||||
SCST BLOCKIO handler has all the necessary support for this behavior. If
|
||||
you write new ALUA state in the "state" attribute, SCST BLOCKIO handler
|
||||
before transition closes the open devices' handles and after transition
|
||||
reopens them, if the new state is active or nonoptimized.
|
||||
|
||||
Thus, the recommended implicit ALUA state change procedure for primary
|
||||
to secondary transition is:
|
||||
|
||||
1. Block all involved SCST devices using "block" sysfs attribute (see
|
||||
above). Wait until the blocking finished.
|
||||
|
||||
2. Change the ALUA state to "transitioning". At this moment all open
|
||||
file handles will be closed.
|
||||
|
||||
3. Perform the DRBD state transition
|
||||
|
||||
4. Change the ALUA state to your desired secondary state.
|
||||
|
||||
5. Unblock the blocked on step 1 devices.
|
||||
|
||||
Optionally, if your initiators support Transitioning ALUA state, for
|
||||
more responsive behavior the blocked devices can be unblocked
|
||||
immediately after step (2). However, not all initiators correctly
|
||||
behave, if they receive ASYMMETRIC STATE TRANSITION sense.
|
||||
|
||||
For the secondary to primary transition procedure is similar.
|
||||
|
||||
In case of explicit ALUA, SCST automatically performs the necessary
|
||||
devices blocking around sending SCST_EVENT_STPG_USER_INVOKE event.
|
||||
|
||||
Checking the Target Configuration
|
||||
.................................
|
||||
|
||||
One way to verify the implicit ALUA configuration from a Linux initiator is
|
||||
via the commands provided in the sg3_utils package. The first step is to
|
||||
verify whether for a certain LUN implicit ALUA has been configured on the
|
||||
target. This is possible by checking whether the TPGS=1 text appears in the
|
||||
sg_inq output, where /dev/sdb is a device node created by the ib_srp initiator:
|
||||
One way to verify the ALUA configuration from a Linux initiator is via
|
||||
the commands provided in the sg3_utils package. The first step is to
|
||||
verify whether for a certain LUN ALUA has been configured on the target.
|
||||
This is possible by checking whether the TPGS=1 text appears in the
|
||||
sg_inq output, where /dev/sdb is a device node created by the ib_srp
|
||||
initiator:
|
||||
|
||||
# sg_inq /dev/sdb
|
||||
standard INQUIRY:
|
||||
@@ -1649,9 +1730,11 @@ Device Identification VPD page:
|
||||
Initiator Support
|
||||
.................
|
||||
|
||||
On Linux systems implicit ALUA support is provided by the scsi_dh_alua kernel
|
||||
driver in combination with the user space multipathd daemon. You will have to
|
||||
modify at least the following in /etc/multipath.conf to enable implicit ALUA:
|
||||
On Linux systems ALUA support is provided by the scsi_dh_alua kernel
|
||||
driver in combination with the user space multipathd daemon. You will
|
||||
have to modify at least the following in /etc/multipath.conf to enable
|
||||
ALUA:
|
||||
|
||||
* hardware_handler "1 alua"
|
||||
* prio alua
|
||||
* path_grouping_policy group_by_prio
|
||||
@@ -1700,6 +1783,126 @@ information about ALUA support in Windows Server, see also:
|
||||
(http://msdn.microsoft.com/en-us/library/gg607458%28v=vs.85%29.aspx).
|
||||
|
||||
|
||||
VAAI
|
||||
----
|
||||
|
||||
SCST supports all 3 VAAI SCSI commands: WRITE SAME, COMPARE AND WRITE
|
||||
(ATS) and EXTENDED COPY. Additionally, it supports not directly related
|
||||
to VAAI Thin Provisioning capabilities, particularly, UNMAP SCSI
|
||||
commands, WRITE SAME with UNMAP bit as well as thin provisioning related
|
||||
devices' sysfs attributes (see above).
|
||||
|
||||
In some cases dev handlers should perform some manual actions to fully
|
||||
benefit from SCST VAAI implementation. Those actions described in the
|
||||
implementation notes below. For vdisk and fileio_tgt handlers they have
|
||||
already been implemented.
|
||||
|
||||
|
||||
Implementation notes
|
||||
....................
|
||||
|
||||
WRITE SAME
|
||||
~~~~~~~~~~
|
||||
|
||||
WRITE SAME command supports 2 modes:
|
||||
|
||||
1. Manual writing mode. In this mode WRITE SAME generates a set of
|
||||
internal WRITE(16) SCSI commands to perform requested writing.
|
||||
|
||||
2. Remap mode. In this mode a dev handler, if supported, can remap being
|
||||
written blocks to a single block and then tell SCST to manually write
|
||||
parts of the requested area, which for some reason can not be remapped.
|
||||
|
||||
In both cases dev handlers should call from WRITE SAME command handler
|
||||
scst_write_same() function. This function as the second argument gets
|
||||
array of descriptors where to write the requested block of data. Last
|
||||
element in this array must have len 0. If this argument is NULL, then
|
||||
the whole area will be manually written by SCST. This value should be
|
||||
used by dev handlers not supporting remapping blocks.
|
||||
|
||||
User space dev handlers should use SCST_EXEC_REPLY_DO_WRITE_SAME
|
||||
reply_type of SCST_USER_EXEC subcommand. See scst_user doc for more
|
||||
info.
|
||||
|
||||
|
||||
COMPARE AND WRITE
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
COMPARE AND WRITE implemented by SCST a set of read, compare and write
|
||||
actions done in atomic manner against affected blocks as well as regular
|
||||
RESERVE SCSI commands. Particularly, COMPARE AND WRITE doesn't need any
|
||||
queue flushing and unlimited number of COMPARE AND WRITE commands on
|
||||
different blocks can be executed simultaneously.
|
||||
|
||||
The read and write actions implemented as generation of internal
|
||||
READ(16) and WRITE(16) SCSI commands.
|
||||
|
||||
COMPARE AND WRITE command is completely transparent to dev handlers
|
||||
(they only see the corresponding READ(16) and WRITE(16) commands), so
|
||||
doesn't require any manual actions from them.
|
||||
|
||||
|
||||
EXTENDED COPY
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
SCST implements EXTENDED COPY via internal Copy Manager target. This
|
||||
target has the following specific attributes in its sysfs:
|
||||
|
||||
- allow_not_connected_copy - if not set (default), an initiator can
|
||||
perform copy only between devices it has direct access to via any
|
||||
target/session. If set, any initiator can copy between any devices in
|
||||
the system.
|
||||
|
||||
- mgmt - this attribute allows to control data from which devices can
|
||||
be copied using Copy Manager. By default, devices belonging to dev
|
||||
handlers with flag auto_cm_assignment_possible set are auto assigned to
|
||||
the Copy Manager on the registration. Currently, only vdisk has this
|
||||
flag set, so all other devices (pass-through, user space, etc.) should
|
||||
be assigned to the Copy Manager manually.
|
||||
|
||||
Mgmt attribute supports the following commands:
|
||||
|
||||
- add [vname|H:C:I:L] - adds device with name vname or H:C:I:L numbers
|
||||
(pass-through) to the Copy Manager.
|
||||
|
||||
- del [vname|H:C:I:L] - deletes device with name vname or H:C:I:L numbers
|
||||
(pass-through) from the Copy Manager.
|
||||
|
||||
|
||||
Internally SCST implements EXTENDED COPY as generation of sets of
|
||||
internal READ(16) and WRITE(16) SCSI commands. Dev handlers don't need
|
||||
any manual actions to use it.
|
||||
|
||||
|
||||
Also SCST provides for dev handlers possibility to remap blocks instead
|
||||
of copy them, if they support this feature. It allows them to perform
|
||||
EXTENDED COPY command much faster by just metadata update of their
|
||||
backend storage, which supposed to be nearly instantaneous.
|
||||
|
||||
To use this feature, a dev handler should setup ext_copy_remap()
|
||||
callback in its struct scst_dev_type. This callback is called by SCST
|
||||
during EXTENDED COPY command processing to let the dev handler try to
|
||||
remap affected blocks at first.
|
||||
|
||||
Upon finish, the dev handler should call scst_ext_copy_remap_done(). In
|
||||
case of error, the dev handler should set the corresponding sense to cmd
|
||||
and then also call scst_ext_copy_remap_done(cmd, NULL, 0).
|
||||
|
||||
If dev handler is not able to remap any part of the segment, if should
|
||||
kmalloc(), then fill all leftover subsegments and supply them to
|
||||
scst_ext_copy_remap_done(). SCST then will copy the subsegments using
|
||||
internal copy machine, then kfree() the supplied array. If the dev
|
||||
handler is not able to remap the whole segment, it can simply directly
|
||||
supply the original segment to scst_ext_copy_remap_done().
|
||||
|
||||
It is highly recommended that in normal circumstances dev handlers call
|
||||
scst_ext_copy_remap_done() from another thread context than one where
|
||||
ext_copy_remap() callback was originally called, because otherwise there
|
||||
could be recursion in the segments processing. Hopefully, this thread
|
||||
context switch is natural for such potentially long operation as
|
||||
EXTENDED COPY.
|
||||
|
||||
|
||||
Caching
|
||||
-------
|
||||
|
||||
|
||||
@@ -143,33 +143,6 @@ change the t10_dev_id if cluster mode has been enabled.
|
||||
Testing
|
||||
-------
|
||||
|
||||
A quick test to verify whether PR synchronization is working is as follows:
|
||||
* Start SCST and enable cluster_mode for a volume on both nodes.
|
||||
* Log in from an initiator system to the two target systems.
|
||||
* Look up the names of the paths on the initiator system with lsscsi. Assume
|
||||
that these names are /dev/sdb and /dev/sdc.
|
||||
* Run the following commands on the initiator system:
|
||||
# sg_persist -s /dev/sdc
|
||||
# sg_persist -n -o --register --param-sark 0xabc123 /dev/sdb
|
||||
# sg_persist -s /dev/sdc
|
||||
SCST_BIO ram0 310
|
||||
Peripheral device type: disk
|
||||
PR generation=0x1
|
||||
Key=0xabc123
|
||||
All target ports bit clear
|
||||
Relative port address: 0x1
|
||||
not reservation holder
|
||||
Transport Id of initiator:
|
||||
iSCSI name and session id: iqn...
|
||||
* Run the following command on a target system:
|
||||
# dlm_tool ls
|
||||
dlm lockspaces
|
||||
name scst-179ee149-ram0
|
||||
id 0x2b2d1c39
|
||||
flags 0x00000000
|
||||
change member 2 joined 1 remove 0 failed 0 seq 1,1
|
||||
members 1 2
|
||||
|
||||
Two examples of test suites for the cluster PR support code are:
|
||||
* The SCSI conformance tests in the libiscsi project.
|
||||
* The Windows Cluster Validation Tests
|
||||
|
||||
+232
-29
@@ -367,6 +367,8 @@ documentation for your dev handlers for more info about it as well as
|
||||
SysfsRules file for more info about common to all dev handlers rules.
|
||||
SCST dev handlers can have the following common entries:
|
||||
|
||||
- block - allows to temporary block and unblock this device. See below.
|
||||
|
||||
- exported - subdirectory containing links to all LUNs where this
|
||||
device was exported.
|
||||
|
||||
@@ -390,6 +392,35 @@ SCST dev handlers can have the following common entries:
|
||||
|
||||
- type - SCSI type of this device
|
||||
|
||||
Attribute "block" allows to temporary block and unblock this device.
|
||||
"Blocking" means that no new commands for this device will go into the
|
||||
execution stage, but instead will be suspended just before it. The
|
||||
blocked state is not reached until queue of the corresponding device is
|
||||
completely drained. You can also call this state "frozen". It is useful
|
||||
in many cases, like consistent snapshots and graceful shutdown.
|
||||
|
||||
On write "block" entry allows the following 3 types of parameters:
|
||||
|
||||
- 1 - block device synchronously, i.e. don't return until this device
|
||||
becomes blocked, i.e. until queue of it is not completely drained. Can
|
||||
be called as many times as needed.
|
||||
|
||||
- 11 params - block device asynchronously, i.e. return immediately.
|
||||
Notification about completing is delivered using SCST_EVENT_EXT_BLOCKING_DONE
|
||||
event. "Params" delivered to it as is in "data" payload. Can be
|
||||
called as many times as needed. Alternatively, status of blocking could be
|
||||
polled by reading this attributes until the second number reaches 0
|
||||
(see below).
|
||||
|
||||
- 0 - unblock this device.
|
||||
|
||||
Reading from "block" entry returns two numbers separated by space:
|
||||
|
||||
1. How many times this device was blocked, i.e. how many times writing
|
||||
"0" to it is needed to unblock this device.
|
||||
|
||||
2. Boolean (0 or 1) if blocking, if any, is done (0) or still pending (1).
|
||||
|
||||
See below for more information about other entries of this subdirectory
|
||||
of the standard SCST dev handlers.
|
||||
|
||||
@@ -1110,6 +1141,7 @@ Each vdisk_fileio's device has the following attributes in
|
||||
For example:
|
||||
|
||||
/sys/kernel/scst_tgt/devices/disk1
|
||||
|-- block
|
||||
|-- blocksize
|
||||
|-- exported
|
||||
| |-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/0
|
||||
@@ -1268,16 +1300,18 @@ persistent reservations from this device are released, upon reconnect
|
||||
the initiators will see it.
|
||||
|
||||
|
||||
Implicit ALUA Support
|
||||
---------------------
|
||||
ALUA Support
|
||||
------------
|
||||
|
||||
SCST supports implicit asymmetric logical unit access (ALUA). Implicit ALUA is
|
||||
a feature defined by the ANSI T10 SCSI committee that allows a target to tell
|
||||
the initiator which path to use in a multipath setup. The redundant paths
|
||||
between initiator and target can be used either for redundancy or for load
|
||||
sharing purposes. The target can either be a single target system running SCST
|
||||
with multiple communication interfaces or two target systems each running SCST
|
||||
and configured in a high availability setup.
|
||||
SCST supports both implicit and explicit asymmetric logical unit access
|
||||
(ALUA). ALUA is a feature defined by the ANSI T10 SCSI committee. It
|
||||
allows a target to tell the initiator which path to use in a multipath
|
||||
setup plus, in the explicit case, control state of each path via SET
|
||||
TARGET PORT GROUPS SCSI command. The redundant paths between initiator
|
||||
and target can be used either for redundancy or for load sharing
|
||||
purposes. The target can either be a single target system running SCST
|
||||
with multiple communication interfaces or two target systems each
|
||||
running SCST and configured in a high availability setup.
|
||||
|
||||
In the SPC-4 standard the following concepts are defined related to ALUA:
|
||||
* Relative target port ID. A number between 1 and 65535 that uniquely
|
||||
@@ -1298,17 +1332,17 @@ ANSI T10 standard called SPC-4.
|
||||
ALUA support in SCST
|
||||
....................
|
||||
|
||||
SCST allows to define implicit ALUA settings for each unique combination of
|
||||
SCST device and SCST target. An initiator however queries ALUA settings by
|
||||
sending an appropriate SCSI command to a specific LUN of an SCST target. Each
|
||||
such LUN maps uniquely to an SCST device. For hardware SCST target drivers,
|
||||
e.g. ib_srpt, there is a one-to-one correspondence between SCST target and
|
||||
SCSI target port. With other SCST targets, e.g. iSCSI-SCST, by default the
|
||||
only relationship between SCST targets and SCSI target ports is that all SCST
|
||||
targets defined on a system are visible via all SCSI target ports. See also
|
||||
the iSCSI-SCST documentation about the allowed_portal attribute for
|
||||
information about how to associate iSCSI targets with a single physical
|
||||
interface.
|
||||
SCST allows to define ALUA settings for each unique combination of SCST
|
||||
device and SCST target. An initiator however queries ALUA settings by
|
||||
sending an appropriate SCSI command to a specific LUN of an SCST target.
|
||||
Each such LUN maps uniquely to an SCST device. For hardware SCST target
|
||||
drivers, e.g. ib_srpt, there is a one-to-one correspondence between SCST
|
||||
target and SCSI target port. With other SCST targets, e.g. iSCSI-SCST,
|
||||
by default the only relationship between SCST targets and SCSI target
|
||||
ports is that all SCST targets defined on a system are visible via all
|
||||
SCSI target ports. See also the iSCSI-SCST documentation about the
|
||||
allowed_portal attribute for information about how to associate iSCSI
|
||||
targets with a single physical interface.
|
||||
|
||||
Notes:
|
||||
- In a H.A. setup it is the responsibility of the user to synchronize ALUA
|
||||
@@ -1331,7 +1365,7 @@ Notes:
|
||||
Configuring ALUA in SCST
|
||||
........................
|
||||
|
||||
SCST allows to configure the following settings related to implicit ALUA
|
||||
SCST allows to configure the following settings related to ALUA
|
||||
for each unique combination of SCST target and virtual SCST device
|
||||
(vdisk_fileio, vdisk_blockio, vcdrom, ...):
|
||||
* The target port group asymmetric access state. SCST supports all ALUA port
|
||||
@@ -1429,15 +1463,62 @@ DEVICE_GROUP dgroup2 {
|
||||
}
|
||||
}
|
||||
|
||||
Explicit ALUA
|
||||
.............
|
||||
|
||||
To enable explicit ALUA you need in addition to the above settings set
|
||||
expl_alua device attribute to 1 (by default it is 0). Also you need to
|
||||
run stpgd and supply to it path to a script or program, which will
|
||||
perform actual path state switching on SET TARGET PORT GROUPS command,
|
||||
for instance, by calling drbdadm. For more information see stpgd README
|
||||
as well as sample script scst_on_stpg.
|
||||
|
||||
DRBD compatibility
|
||||
..................
|
||||
|
||||
DRBD does not allow to open its device on the secondary as well as does
|
||||
not allow to perform primary to secondary transition, if this device is
|
||||
open.
|
||||
|
||||
SCST BLOCKIO handler has all the necessary support for this behavior. If
|
||||
you write new ALUA state in the "state" attribute, SCST BLOCKIO handler
|
||||
before transition closes the open devices' handles and after transition
|
||||
reopens them, if the new state is active or nonoptimized.
|
||||
|
||||
Thus, the recommended implicit ALUA state change procedure for primary
|
||||
to secondary transition is:
|
||||
|
||||
1. Block all involved SCST devices using "block" sysfs attribute (see
|
||||
above). Wait until the blocking finished.
|
||||
|
||||
2. Change the ALUA state to "transitioning". At this moment all open
|
||||
file handles will be closed.
|
||||
|
||||
3. Perform the DRBD state transition
|
||||
|
||||
4. Change the ALUA state to your desired secondary state.
|
||||
|
||||
5. Unblock the blocked on step 1 devices.
|
||||
|
||||
Optionally, if your initiators support Transitioning ALUA state, for
|
||||
more responsive behavior the blocked devices can be unblocked
|
||||
immediately after step (2). However, not all initiators correctly
|
||||
behave, if they receive ASYMMETRIC STATE TRANSITION sense.
|
||||
|
||||
For the secondary to primary transition procedure is similar.
|
||||
|
||||
In case of explicit ALUA, SCST automatically performs the necessary
|
||||
devices blocking around sending SCST_EVENT_STPG_USER_INVOKE event.
|
||||
|
||||
Checking the Target Configuration
|
||||
.................................
|
||||
|
||||
One way to verify the implicit ALUA configuration from a Linux initiator is
|
||||
via the commands provided in the sg3_utils package. The first step is to
|
||||
verify whether for a certain LUN implicit ALUA has been configured on the
|
||||
target. This is possible by checking whether the TPGS=1 text appears in the
|
||||
sg_inq output, where /dev/sdb is a device node created by the ib_srp initiator:
|
||||
One way to verify the ALUA configuration from a Linux initiator is via
|
||||
the commands provided in the sg3_utils package. The first step is to
|
||||
verify whether for a certain LUN ALUA has been configured on the target.
|
||||
This is possible by checking whether the TPGS=1 text appears in the
|
||||
sg_inq output, where /dev/sdb is a device node created by the ib_srp
|
||||
initiator:
|
||||
|
||||
# sg_inq /dev/sdb
|
||||
standard INQUIRY:
|
||||
@@ -1497,9 +1578,11 @@ Device Identification VPD page:
|
||||
Initiator Support
|
||||
.................
|
||||
|
||||
On Linux systems implicit ALUA support is provided by the scsi_dh_alua kernel
|
||||
driver in combination with the user space multipathd daemon. You will have to
|
||||
modify at least the following in /etc/multipath.conf to enable implicit ALUA:
|
||||
On Linux systems ALUA support is provided by the scsi_dh_alua kernel
|
||||
driver in combination with the user space multipathd daemon. You will
|
||||
have to modify at least the following in /etc/multipath.conf to enable
|
||||
ALUA:
|
||||
|
||||
* hardware_handler "1 alua"
|
||||
* prio alua
|
||||
* path_grouping_policy group_by_prio
|
||||
@@ -1548,6 +1631,126 @@ information about ALUA support in Windows Server, see also:
|
||||
(http://msdn.microsoft.com/en-us/library/gg607458%28v=vs.85%29.aspx).
|
||||
|
||||
|
||||
VAAI
|
||||
----
|
||||
|
||||
SCST supports all 3 VAAI SCSI commands: WRITE SAME, COMPARE AND WRITE
|
||||
(ATS) and EXTENDED COPY. Additionally, it supports not directly related
|
||||
to VAAI Thin Provisioning capabilities, particularly, UNMAP SCSI
|
||||
commands, WRITE SAME with UNMAP bit as well as thin provisioning related
|
||||
devices' sysfs attributes (see above).
|
||||
|
||||
In some cases dev handlers should perform some manual actions to fully
|
||||
benefit from SCST VAAI implementation. Those actions described in the
|
||||
implementation notes below. For vdisk and fileio_tgt handlers they have
|
||||
already been implemented.
|
||||
|
||||
|
||||
Implementation notes
|
||||
....................
|
||||
|
||||
WRITE SAME
|
||||
~~~~~~~~~~
|
||||
|
||||
WRITE SAME command supports 2 modes:
|
||||
|
||||
1. Manual writing mode. In this mode WRITE SAME generates a set of
|
||||
internal WRITE(16) SCSI commands to perform requested writing.
|
||||
|
||||
2. Remap mode. In this mode a dev handler, if supported, can remap being
|
||||
written blocks to a single block and then tell SCST to manually write
|
||||
parts of the requested area, which for some reason can not be remapped.
|
||||
|
||||
In both cases dev handlers should call from WRITE SAME command handler
|
||||
scst_write_same() function. This function as the second argument gets
|
||||
array of descriptors where to write the requested block of data. Last
|
||||
element in this array must have len 0. If this argument is NULL, then
|
||||
the whole area will be manually written by SCST. This value should be
|
||||
used by dev handlers not supporting remapping blocks.
|
||||
|
||||
User space dev handlers should use SCST_EXEC_REPLY_DO_WRITE_SAME
|
||||
reply_type of SCST_USER_EXEC subcommand. See scst_user doc for more
|
||||
info.
|
||||
|
||||
|
||||
COMPARE AND WRITE
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
COMPARE AND WRITE implemented by SCST a set of read, compare and write
|
||||
actions done in atomic manner against affected blocks as well as regular
|
||||
RESERVE SCSI commands. Particularly, COMPARE AND WRITE doesn't need any
|
||||
queue flushing and unlimited number of COMPARE AND WRITE commands on
|
||||
different blocks can be executed simultaneously.
|
||||
|
||||
The read and write actions implemented as generation of internal
|
||||
READ(16) and WRITE(16) SCSI commands.
|
||||
|
||||
COMPARE AND WRITE command is completely transparent to dev handlers
|
||||
(they only see the corresponding READ(16) and WRITE(16) commands), so
|
||||
doesn't require any manual actions from them.
|
||||
|
||||
|
||||
EXTENDED COPY
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
SCST implements EXTENDED COPY via internal Copy Manager target. This
|
||||
target has the following specific attributes in its sysfs:
|
||||
|
||||
- allow_not_connected_copy - if not set (default), an initiator can
|
||||
perform copy only between devices it has direct access to via any
|
||||
target/session. If set, any initiator can copy between any devices in
|
||||
the system.
|
||||
|
||||
- mgmt - this attribute allows to control data from which devices can
|
||||
be copied using Copy Manager. By default, devices belonging to dev
|
||||
handlers with flag auto_cm_assignment_possible set are auto assigned to
|
||||
the Copy Manager on the registration. Currently, only vdisk has this
|
||||
flag set, so all other devices (pass-through, user space, etc.) should
|
||||
be assigned to the Copy Manager manually.
|
||||
|
||||
Mgmt attribute supports the following commands:
|
||||
|
||||
- add [vname|H:C:I:L] - adds device with name vname or H:C:I:L numbers
|
||||
(pass-through) to the Copy Manager.
|
||||
|
||||
- del [vname|H:C:I:L] - deletes device with name vname or H:C:I:L numbers
|
||||
(pass-through) from the Copy Manager.
|
||||
|
||||
|
||||
Internally SCST implements EXTENDED COPY as generation of sets of
|
||||
internal READ(16) and WRITE(16) SCSI commands. Dev handlers don't need
|
||||
any manual actions to use it.
|
||||
|
||||
|
||||
Also SCST provides for dev handlers possibility to remap blocks instead
|
||||
of copy them, if they support this feature. It allows them to perform
|
||||
EXTENDED COPY command much faster by just metadata update of their
|
||||
backend storage, which supposed to be nearly instantaneous.
|
||||
|
||||
To use this feature, a dev handler should setup ext_copy_remap()
|
||||
callback in its struct scst_dev_type. This callback is called by SCST
|
||||
during EXTENDED COPY command processing to let the dev handler try to
|
||||
remap affected blocks at first.
|
||||
|
||||
Upon finish, the dev handler should call scst_ext_copy_remap_done(). In
|
||||
case of error, the dev handler should set the corresponding sense to cmd
|
||||
and then also call scst_ext_copy_remap_done(cmd, NULL, 0).
|
||||
|
||||
If dev handler is not able to remap any part of the segment, if should
|
||||
kmalloc(), then fill all leftover subsegments and supply them to
|
||||
scst_ext_copy_remap_done(). SCST then will copy the subsegments using
|
||||
internal copy machine, then kfree() the supplied array. If the dev
|
||||
handler is not able to remap the whole segment, it can simply directly
|
||||
supply the original segment to scst_ext_copy_remap_done().
|
||||
|
||||
It is highly recommended that in normal circumstances dev handlers call
|
||||
scst_ext_copy_remap_done() from another thread context than one where
|
||||
ext_copy_remap() callback was originally called, because otherwise there
|
||||
could be recursion in the segments processing. Hopefully, this thread
|
||||
context switch is natural for such potentially long operation as
|
||||
EXTENDED COPY.
|
||||
|
||||
|
||||
Caching
|
||||
-------
|
||||
|
||||
|
||||
@@ -255,6 +255,32 @@ static inline void hex2bin(u8 *dst, const char *src, size_t count)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)
|
||||
static inline int __must_check kstrtoull(const char *s, unsigned int base,
|
||||
unsigned long long *res)
|
||||
{
|
||||
return strict_strtoull(s, base, res);
|
||||
}
|
||||
|
||||
static inline int __must_check kstrtoll(const char *s, unsigned int base,
|
||||
long long *res)
|
||||
{
|
||||
return strict_strtoll(s, base, res);
|
||||
}
|
||||
|
||||
static inline int __must_check kstrtoul(const char *s, unsigned int base,
|
||||
unsigned long *res)
|
||||
{
|
||||
return strict_strtoul(s, base, res);
|
||||
}
|
||||
|
||||
static inline int __must_check kstrtol(const char *s, unsigned int base,
|
||||
long *res)
|
||||
{
|
||||
return strict_strtol(s, base, res);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* <linux/list.h> */
|
||||
|
||||
#ifndef __list_for_each
|
||||
@@ -401,6 +427,15 @@ static inline void sg_set_page(struct scatterlist *sg, struct page *page,
|
||||
(__flags), NULL, NULL)
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
|
||||
static inline void *kmalloc_array(size_t n, size_t size, gfp_t flags)
|
||||
{
|
||||
if (size != 0 && n > ULONG_MAX / size)
|
||||
return NULL;
|
||||
return kmalloc(n * size, flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* <linux/t10-pi.h> */
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0)
|
||||
|
||||
+211
-10
@@ -684,6 +684,7 @@ struct scst_acg;
|
||||
struct scst_acg_dev;
|
||||
struct scst_acn;
|
||||
struct scst_aen;
|
||||
struct scst_ext_copy_seg_descr;
|
||||
struct scst_opcode_descriptor;
|
||||
|
||||
/*
|
||||
@@ -1330,6 +1331,12 @@ struct scst_dev_type {
|
||||
*/
|
||||
unsigned pr_cmds_notifications:1;
|
||||
|
||||
/*
|
||||
* Set if the Copy Manager can auto assing to devices of this
|
||||
* template on their registration.
|
||||
*/
|
||||
unsigned auto_cm_assignment_possible:1;
|
||||
|
||||
/*
|
||||
* Called to parse CDB from the cmd and initialize
|
||||
* cmd->bufflen and cmd->data_direction (both - REQUIRED).
|
||||
@@ -1420,6 +1427,50 @@ struct scst_dev_type {
|
||||
*/
|
||||
void (*on_free_cmd)(struct scst_cmd *cmd);
|
||||
|
||||
/*
|
||||
* Called during EXTENDED COPY command processing to let dev handler
|
||||
* try to remap blocks at first. Upon finish, the dev handler supposed
|
||||
* to call scst_ext_copy_remap_done(). See description of this
|
||||
* function for more details.
|
||||
*
|
||||
* In case of error, the dev handler should set the corresponding sense
|
||||
* to cmd and then call scst_ext_copy_remap_done(cmd, NULL, 0).
|
||||
*
|
||||
* It is highly recommended that in the normal circumstances
|
||||
* scst_ext_copy_remap_done() called from another thread context,
|
||||
* because otherwise there will be recursion in the segments processing.
|
||||
* Hopefully, this thread context switch is natural for such
|
||||
* potentially long operation.
|
||||
*
|
||||
* OPTIONAL
|
||||
*/
|
||||
void (*ext_copy_remap)(struct scst_cmd *cmd,
|
||||
struct scst_ext_copy_seg_descr *descr);
|
||||
|
||||
/*
|
||||
* Called to notify dev handler that a ALUA state change is about to
|
||||
* be started. Can be used to close open file handlers, which might
|
||||
* prevent the state switch.
|
||||
*
|
||||
* Called under scst_dg_mutex and no activities on the dev handler level.
|
||||
*
|
||||
* OPTIONAL
|
||||
*/
|
||||
void (*on_alua_state_change_start)(struct scst_device *dev,
|
||||
enum scst_tg_state old_state, enum scst_tg_state new_state);
|
||||
|
||||
/*
|
||||
* Called to notify dev handler that a ALUA state change is about to
|
||||
* be finished. Can be used to (re)open file handlers closed in
|
||||
* on_alua_state_change_start().
|
||||
*
|
||||
* Called under scst_dg_mutex and no activities on the dev handler level.
|
||||
*
|
||||
* OPTIONAL
|
||||
*/
|
||||
void (*on_alua_state_change_finish)(struct scst_device *dev,
|
||||
enum scst_tg_state old_state, enum scst_tg_state new_state);
|
||||
|
||||
/*
|
||||
* Called to notify dev handler that a task management command received
|
||||
*
|
||||
@@ -1918,6 +1969,14 @@ struct scst_session {
|
||||
/* Used if scst_unregister_session() called in wait mode */
|
||||
struct completion *shutdown_compl;
|
||||
|
||||
/*
|
||||
* Keep list IDs for Extended Copy commands sent over this session.
|
||||
* Protected by scst_cm_lock.
|
||||
*/
|
||||
struct list_head sess_cm_list_id_list;
|
||||
|
||||
struct delayed_work sess_cm_list_id_cleanup_work;
|
||||
|
||||
/* sysfs release completion */
|
||||
struct completion *sess_kobj_release_cmpl;
|
||||
|
||||
@@ -2110,12 +2169,24 @@ struct scst_cmd {
|
||||
/* Set if cmd is internally generated */
|
||||
unsigned int internal:1;
|
||||
|
||||
/* Set if local events should be checked for internally generated cmd */
|
||||
unsigned int internal_check_local_events:1;
|
||||
|
||||
/* Set if the blocking machinery should be bypassed for this cmd */
|
||||
unsigned int bypass_blocking:1;
|
||||
|
||||
/* Set if the device was blocked by scst_check_blocked_dev() */
|
||||
unsigned int unblock_dev:1;
|
||||
|
||||
/* Set if scst_dec_on_dev_cmd() call is needed on the cmd's finish */
|
||||
unsigned int dec_on_dev_needed:1;
|
||||
|
||||
/* Set if cmd is on dev's exec_cmd_list */
|
||||
unsigned int on_dev_exec_list:1;
|
||||
|
||||
/* Set if this cmd passed check for SCSI atomicity */
|
||||
unsigned int scsi_atomicity_checked:1;
|
||||
|
||||
/* Set if cmd is queued as hw pending */
|
||||
unsigned int cmd_hw_pending:1;
|
||||
|
||||
@@ -2208,12 +2279,21 @@ struct scst_cmd {
|
||||
/* Set if cmd was pre-alloced by target driver */
|
||||
unsigned int pre_alloced:1;
|
||||
|
||||
/* Set if cmd was already ALUA checked in TRANSITIONING state */
|
||||
unsigned int already_transitioning:1;
|
||||
|
||||
/* Set if scst_cmd_set_write_not_received_data_len() was called */
|
||||
unsigned int write_not_received_set:1;
|
||||
|
||||
/* Set if cmd has LINK bit set in CDB */
|
||||
unsigned int cmd_linked:1;
|
||||
|
||||
/* Set if cmd is on scst_global_stpg_list */
|
||||
unsigned int cmd_on_global_stpg_list:1;
|
||||
|
||||
/* Set if cmd was globally STPG blocked in __scst_check_blocked_dev() */
|
||||
unsigned int cmd_global_stpg_blocked:1;
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
/* cmd's async flags */
|
||||
@@ -2269,6 +2349,15 @@ struct scst_cmd {
|
||||
unsigned short cdb_len;
|
||||
uint8_t cdb_buf[SCST_MAX_CDB_SIZE];
|
||||
|
||||
/* List entry for dev's dev_exec_cmd_list */
|
||||
struct list_head dev_exec_cmd_list_entry;
|
||||
|
||||
/*
|
||||
* Array of blocked by this cmd SCSI atomic cmds with size
|
||||
* scsi_atomic_blocked_cmds_count. Protected by dev->dev_lock.
|
||||
*/
|
||||
struct scst_cmd **scsi_atomic_blocked_cmds;
|
||||
|
||||
uint8_t lba_off; /* LBA offset in cdb */
|
||||
uint8_t lba_len; /* LBA length in cdb */
|
||||
uint8_t len_off; /* length offset in cdb */
|
||||
@@ -2400,6 +2489,18 @@ struct scst_cmd {
|
||||
/* Used for storage of dev handler private stuff */
|
||||
void *dh_priv;
|
||||
|
||||
/*
|
||||
* Number of waiting for this cmd to finish commands
|
||||
* with SCSI atomic guarantees. Protected by dev->dev_lock.
|
||||
*/
|
||||
int scsi_atomic_blockers;
|
||||
|
||||
/*
|
||||
* How many SCSI atomic cmds this cmd blocked, i.e. size of array
|
||||
* scsi_atomic_blocked_cmds. Protected by dev->dev_lock.
|
||||
*/
|
||||
int scsi_atomic_blocked_cmds_count;
|
||||
|
||||
/* List entry for dev's blocked_cmd_list */
|
||||
struct list_head blocked_cmd_list_entry;
|
||||
|
||||
@@ -2429,6 +2530,11 @@ struct scst_cmd {
|
||||
void *cmd_data_descriptors;
|
||||
int cmd_data_descriptors_cnt;
|
||||
};
|
||||
|
||||
/* STPG commands global serialization */
|
||||
struct {
|
||||
struct list_head global_stpg_list_entry;
|
||||
};
|
||||
};
|
||||
|
||||
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
|
||||
@@ -2440,6 +2546,7 @@ struct scst_cmd {
|
||||
uint64_t restart_waiting_time, rdy_to_xfer_time;
|
||||
uint64_t pre_exec_time, exec_time, dev_done_time;
|
||||
uint64_t xmit_time;
|
||||
bool exec_time_counting;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SCST_DEBUG_TM
|
||||
@@ -2611,6 +2718,20 @@ struct scst_cl_ops {
|
||||
void (*reserve)(struct scst_device *dev, struct scst_session *sess);
|
||||
};
|
||||
|
||||
/*
|
||||
* Extended, e.g. via sysfs, blockers
|
||||
*/
|
||||
typedef void (*ext_blocker_done_fn_t) (struct scst_device *dev,
|
||||
uint8_t *data, int len);
|
||||
|
||||
struct scst_ext_blocker {
|
||||
struct list_head ext_blockers_list_entry;
|
||||
|
||||
ext_blocker_done_fn_t ext_blocker_done_fn;
|
||||
int ext_blocker_data_len;
|
||||
uint8_t ext_blocker_data[];
|
||||
};
|
||||
|
||||
/*
|
||||
* SCST device
|
||||
*/
|
||||
@@ -2631,6 +2752,26 @@ struct scst_device {
|
||||
/* Set, if a strictly serialized cmd is waiting blocked */
|
||||
unsigned int strictly_serialized_cmd_waiting:1;
|
||||
|
||||
/*
|
||||
* Set, if this device is being unregistered. Useful to let sysfs
|
||||
* attributes know when they should exit immediately to prevent
|
||||
* possible deadlocks with their device unregistration waiting for
|
||||
* their kobj last put.
|
||||
*/
|
||||
unsigned int dev_unregistering:1;
|
||||
|
||||
/*
|
||||
* Set if ext blocking is pending. It if just shortcut for
|
||||
* !list_empty(&dev->ext_blockers_list) to save a cache miss.
|
||||
*/
|
||||
unsigned int ext_blocking_pending:1;
|
||||
|
||||
/* Used to serialize invocations of __scst_ext_blocking_done() */
|
||||
unsigned int ext_unblock_scheduled:1;
|
||||
|
||||
/* Set if this device was blocked during STPG command processing */
|
||||
unsigned int stpg_ext_blocked:1;
|
||||
|
||||
/* Set if this device does not support DIF IP checking */
|
||||
unsigned int dev_dif_ip_not_supported:1;
|
||||
|
||||
@@ -2744,15 +2885,23 @@ struct scst_device {
|
||||
*/
|
||||
int on_dev_cmd_count;
|
||||
|
||||
/*
|
||||
* How many atomic SCSI commands being executed. Protected by dev_lock.
|
||||
*/
|
||||
int dev_scsi_atomic_cmd_active;
|
||||
|
||||
/*
|
||||
* List of all being executed on the dev commands.
|
||||
* Protected by dev_lock.
|
||||
*/
|
||||
struct list_head dev_exec_cmd_list;
|
||||
|
||||
/* Memory limits for this device */
|
||||
struct scst_mem_lim dev_mem_lim;
|
||||
|
||||
/* List of commands with lock, if dedicated threads are used */
|
||||
struct scst_cmd_threads dev_cmd_threads;
|
||||
|
||||
/* Operations that depend on whether or not cluster mode is enabled */
|
||||
const struct scst_cl_ops *cl_ops;
|
||||
|
||||
/*************************************************************
|
||||
** T10-PI fields. Read-only, hence no protection.
|
||||
*************************************************************/
|
||||
@@ -2770,9 +2919,11 @@ struct scst_device {
|
||||
int (*dev_dif_fn)(struct scst_cmd *cmd);
|
||||
|
||||
__be16 dev_dif_static_app_tag; /* fixed APP TAG for all blocks in dev */
|
||||
__be32 dev_dif_static_app_ref_tag; /* fixed APP TAG part from REF
|
||||
* TAG for all blocks in dev.
|
||||
* Valid only with dif type 3 */
|
||||
/*
|
||||
* Fixed APP TAG part from REF TAG for all blocks in dev. Valid only
|
||||
* with dif type 3.
|
||||
*/
|
||||
__be32 dev_dif_static_app_ref_tag;
|
||||
|
||||
/* Cache to optimize scst_parse_*protect() routines */
|
||||
enum scst_dif_actions dev_dif_rd_actions;
|
||||
@@ -2784,6 +2935,9 @@ struct scst_device {
|
||||
/* Set if reserved via the SPC-2 SCSI RESERVE command. */
|
||||
struct scst_session *reserved_by;
|
||||
|
||||
/* Operations that depend on whether or not cluster mode is enabled */
|
||||
const struct scst_cl_ops *cl_ops;
|
||||
|
||||
/**********************************************************************
|
||||
* Persistent reservation fields. Protected as follows:
|
||||
* - Reading PR data must be protected via scst_pr_read_lock() /
|
||||
@@ -2854,6 +3008,15 @@ struct scst_device {
|
||||
/* List of blocked commands, protected by dev_lock. */
|
||||
struct list_head blocked_cmd_list;
|
||||
|
||||
/* Number of ext blocking requests, protected by dev_lock */
|
||||
int ext_blocks_cnt;
|
||||
|
||||
/* List of ext blockers, protected by dev_lock */
|
||||
struct list_head ext_blockers_list;
|
||||
|
||||
/* Work to notify ext blockers out of dev_lock context */
|
||||
struct work_struct ext_blockers_work;
|
||||
|
||||
/* MAXIMUM WRITE SAME LENGTH in bytes */
|
||||
uint64_t max_write_same_len;
|
||||
|
||||
@@ -2969,7 +3132,10 @@ struct scst_tgt_dev {
|
||||
atomic_t tgt_dev_cmd_count ____cacheline_aligned_in_smp;
|
||||
|
||||
/* ALUA command filter */
|
||||
bool (*alua_filter)(struct scst_cmd *cmd);
|
||||
#define SCST_ALUA_CHECK_OK 0
|
||||
#define SCST_ALUA_CHECK_DELAYED 1
|
||||
#define SCST_ALUA_CHECK_ERROR -1
|
||||
int (*alua_filter)(struct scst_cmd *cmd);
|
||||
|
||||
struct scst_order_data *curr_order_data;
|
||||
struct scst_order_data tgt_dev_order_data;
|
||||
@@ -3176,6 +3342,8 @@ struct scst_acn {
|
||||
* @kobj: For making this object visible in sysfs.
|
||||
* @dev_kobj: Sysfs devices directory.
|
||||
* @tg_kobj: Sysfs target groups directory.
|
||||
* @stpg_transport_id Initiator transport ID for STPG originating I_T nexus, if any
|
||||
* @stpg_rel_tgt_id Relative target ID for STPG originating I_T nexus, if any
|
||||
*
|
||||
* Each device is member of zero or one device groups. With each device group
|
||||
* there are zero or more target groups associated.
|
||||
@@ -3188,6 +3356,8 @@ struct scst_dev_group {
|
||||
struct kobject kobj;
|
||||
struct kobject *dev_kobj;
|
||||
struct kobject *tg_kobj;
|
||||
uint8_t *stpg_transport_id;
|
||||
uint16_t stpg_rel_tgt_id;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -3300,6 +3470,7 @@ extern const struct scst_opcode_descriptor scst_op_descr_stpg;
|
||||
extern const struct scst_opcode_descriptor scst_op_descr_send_diagnostic;
|
||||
|
||||
extern const struct scst_opcode_descriptor scst_op_descr_inquiry;
|
||||
extern const struct scst_opcode_descriptor scst_op_descr_extended_copy;
|
||||
extern const struct scst_opcode_descriptor scst_op_descr_tur;
|
||||
extern const struct scst_opcode_descriptor scst_op_descr_reserve6;
|
||||
extern const struct scst_opcode_descriptor scst_op_descr_release6;
|
||||
@@ -3326,7 +3497,6 @@ extern const struct scst_opcode_descriptor scst_op_descr_report_supp_opcodes;
|
||||
&scst_op_descr_report_supp_opcodes, \
|
||||
&scst_op_descr_report_supp_tm_fns,
|
||||
|
||||
|
||||
#ifndef smp_mb__after_set_bit
|
||||
/* There is no smp_mb__after_set_bit() in the kernel */
|
||||
#define smp_mb__after_set_bit() smp_mb()
|
||||
@@ -3655,9 +3825,12 @@ static inline void scst_sess_set_tgt_priv(struct scst_session *sess,
|
||||
}
|
||||
|
||||
uint16_t scst_lookup_tg_id(struct scst_device *dev, struct scst_tgt *tgt);
|
||||
bool scst_impl_alua_configured(struct scst_device *dev);
|
||||
bool scst_alua_configured(struct scst_device *dev);
|
||||
int scst_tg_get_group_info(void **buf, uint32_t *response_length,
|
||||
struct scst_device *dev, uint8_t data_format);
|
||||
int scst_tg_set_group_info(struct scst_cmd *cmd);
|
||||
const char *scst_alua_state_name(enum scst_tg_state s);
|
||||
void scst_stpg_del_unblock_next(struct scst_cmd *cmd);
|
||||
|
||||
/*
|
||||
* Get/set functions for dev's static DIF APP TAG
|
||||
@@ -5453,7 +5626,7 @@ struct scst_data_descriptor {
|
||||
uint64_t sdd_blocks;
|
||||
};
|
||||
|
||||
void scst_write_same(struct scst_cmd *cmd);
|
||||
void scst_write_same(struct scst_cmd *cmd, struct scst_data_descriptor *where);
|
||||
|
||||
__be64 scst_pack_lun(const uint64_t lun, enum scst_lun_addr_method addr_method);
|
||||
uint64_t scst_unpack_lun(const uint8_t *lun, int len);
|
||||
@@ -5478,4 +5651,32 @@ int scst_pr_set_cluster_mode(struct scst_device *dev, bool cluster_mode,
|
||||
int scst_pr_init_dev(struct scst_device *dev);
|
||||
void scst_pr_clear_dev(struct scst_device *dev);
|
||||
|
||||
struct scst_ext_copy_data_descr {
|
||||
uint64_t src_lba;
|
||||
uint64_t dst_lba;
|
||||
int data_len; /* in bytes */
|
||||
};
|
||||
|
||||
struct scst_ext_copy_seg_descr {
|
||||
#define SCST_EXT_COPY_SEG_DATA 0
|
||||
#if 0 /* not implemented yet */
|
||||
#define SCST_EXT_COPY_SEG_PR_REG 1
|
||||
#define SCST_EXT_COPY_SEG_PR_MOVE 2
|
||||
#endif
|
||||
int type;
|
||||
union {
|
||||
struct {
|
||||
struct scst_tgt_dev *src_tgt_dev;
|
||||
struct scst_tgt_dev *dst_tgt_dev;
|
||||
struct scst_ext_copy_data_descr data_descr;
|
||||
};
|
||||
};
|
||||
/* Internal, don't touch! */
|
||||
int tgt_descr_offs;
|
||||
};
|
||||
|
||||
void scst_ext_copy_remap_done(struct scst_cmd *ec_cmd,
|
||||
struct scst_ext_copy_data_descr *dds, int dds_cnt);
|
||||
int scst_ext_copy_get_cur_seg_data_len(struct scst_cmd *ec_cmd);
|
||||
|
||||
#endif /* __SCST_H */
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
#else
|
||||
#define SCST_VERSION_STRING_SUFFIX
|
||||
#endif
|
||||
#define SCST_VERSION_NAME "3.1.0-pre1"
|
||||
#define SCST_VERSION_NAME "3.2.0-pre1"
|
||||
#define SCST_VERSION_STRING SCST_VERSION_NAME SCST_VERSION_STRING_SUFFIX
|
||||
|
||||
#define SCST_CONST_VERSION SCST_CONST_INTF_VER
|
||||
@@ -228,9 +228,11 @@ enum scst_cdb_flags {
|
||||
#ifdef CONFIG_SCST_TEST_IO_IN_SIRQ
|
||||
SCST_TEST_IO_IN_SIRQ_ALLOWED = 0x8000,
|
||||
#endif
|
||||
SCST_SERIALIZED = 0x10000,
|
||||
SCST_STRICTLY_SERIALIZED = 0x20000|SCST_SERIALIZED,
|
||||
SCST_DESCRIPTORS_BASED = 0x40000,
|
||||
SCST_SERIALIZED = 0x10000,
|
||||
SCST_STRICTLY_SERIALIZED = 0x20000|SCST_SERIALIZED,
|
||||
SCST_CAN_GEN_3PARTY_COMMANDS = 0x40000,
|
||||
SCST_DESCRIPTORS_BASED = 0x80000,
|
||||
SCST_SCSI_ATOMIC = 0x100000,
|
||||
};
|
||||
|
||||
/*************************************************************
|
||||
@@ -290,9 +292,9 @@ static inline int scst_sense_response_code(const uint8_t *sense)
|
||||
|
||||
/* NOT_READY is 2 */
|
||||
#define scst_sense_format_in_progress NOT_READY, 0x04, 0x04
|
||||
#define scst_sense_tp_transitioning NOT_READY, 0x04, 0x0A
|
||||
#define scst_sense_tp_standby NOT_READY, 0x04, 0x0B
|
||||
#define scst_sense_tp_unav NOT_READY, 0x04, 0x0C
|
||||
#define scst_sense_alua_transitioning NOT_READY, 0x04, 0x0A
|
||||
#define scst_sense_alua_standby NOT_READY, 0x04, 0x0B
|
||||
#define scst_sense_alua_unav NOT_READY, 0x04, 0x0C
|
||||
#define scst_sense_no_medium NOT_READY, 0x3a, 0
|
||||
|
||||
/* MEDIUM_ERROR is 3 */
|
||||
@@ -301,8 +303,11 @@ static inline int scst_sense_response_code(const uint8_t *sense)
|
||||
|
||||
/* HARDWARE_ERROR is 4 */
|
||||
#define scst_sense_hardw_error HARDWARE_ERROR, 0x44, 0 /* non-retriable */
|
||||
#define scst_sense_set_target_pgs_failed HARDWARE_ERROR, 0x67, 0xA
|
||||
|
||||
/* ILLEGAL_REQUEST is 5 */
|
||||
#define scst_sense_operation_in_progress ILLEGAL_REQUEST, 0x0, 0x16
|
||||
#define scst_sense_parameter_list_length_invalid ILLEGAL_REQUEST, 0x1A, 0
|
||||
#define scst_sense_invalid_opcode ILLEGAL_REQUEST, 0x20, 0
|
||||
#define scst_sense_block_out_range_error ILLEGAL_REQUEST, 0x21, 0
|
||||
/* Don't use it directly, use scst_set_invalid_field_in_cdb() instead! */
|
||||
@@ -312,6 +317,11 @@ static inline int scst_sense_response_code(const uint8_t *sense)
|
||||
#define scst_sense_invalid_field_in_parm_list ILLEGAL_REQUEST, 0x26, 0
|
||||
#define scst_sense_parameter_value_invalid ILLEGAL_REQUEST, 0x26, 2
|
||||
#define scst_sense_invalid_release ILLEGAL_REQUEST, 0x26, 4
|
||||
#define scst_sense_too_many_target_descriptors ILLEGAL_REQUEST, 0x26, 6
|
||||
#define scst_sense_unsupported_tgt_descr_type ILLEGAL_REQUEST, 0x26, 7
|
||||
#define scst_sense_too_many_segment_descriptors ILLEGAL_REQUEST, 0x26, 8
|
||||
#define scst_sense_unsupported_seg_descr_type ILLEGAL_REQUEST, 0x26, 9
|
||||
#define scst_sense_inline_data_length_exceeded ILLEGAL_REQUEST, 0x26, 0xB
|
||||
#define scst_sense_saving_params_unsup ILLEGAL_REQUEST, 0x39, 0
|
||||
#define scst_sense_invalid_message ILLEGAL_REQUEST, 0x49, 0
|
||||
#define scst_sense_parameter_list_length_invalid ILLEGAL_REQUEST, 0x1A, 0
|
||||
@@ -419,6 +429,14 @@ static inline int scst_sense_response_code(const uint8_t *sense)
|
||||
#define WRITE_SAME_16 0x93
|
||||
#endif
|
||||
|
||||
#ifndef EXTENDED_COPY
|
||||
#define EXTENDED_COPY 0x83
|
||||
#endif
|
||||
|
||||
#ifndef RECEIVE_COPY_RESULTS
|
||||
#define RECEIVE_COPY_RESULTS 0x84
|
||||
#endif
|
||||
|
||||
#ifndef SYNCHRONIZE_CACHE_16
|
||||
#define SYNCHRONIZE_CACHE_16 0x91
|
||||
#endif
|
||||
@@ -588,6 +606,12 @@ enum {
|
||||
#define SCSI_TRANSPORTID_PROTOCOLID_ISCSI 5
|
||||
#define SCSI_TRANSPORTID_PROTOCOLID_SAS 6
|
||||
|
||||
/*
|
||||
* SCST extension. Added, because there is no TransportID for Copy Manager
|
||||
* defined anywhere in the SCSI standards.
|
||||
*/
|
||||
#define SCST_TRANSPORTID_PROTOCOLID_COPY_MGR 0xC
|
||||
|
||||
/**
|
||||
* enum scst_tg_state - SCSI target port group asymmetric access state.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* include/scst_event.h
|
||||
*
|
||||
* Copyright (C) 2014 - 2015 SanDisk Corporation
|
||||
*
|
||||
* Contains constants and data structures for scst_event module.
|
||||
*/
|
||||
|
||||
#ifndef __SCST_EVENT_H
|
||||
#define __SCST_EVENT_H
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/types.h>
|
||||
#endif
|
||||
|
||||
#ifdef INSIDE_KERNEL_TREE
|
||||
#include <scst/scst_const.h>
|
||||
#else
|
||||
#include "scst_const.h"
|
||||
#endif
|
||||
|
||||
#define SCST_EVENT_NAME "scst_event"
|
||||
#define SCST_EVENT_PATH "/dev/"
|
||||
#define SCST_EVENT_DEV SCST_EVENT_PATH SCST_EVENT_NAME
|
||||
#define SCST_EVENT_VERSION_NAME SCST_VERSION_NAME
|
||||
#define SCST_EVENT_VERSION \
|
||||
SCST_EVENT_VERSION_NAME "$Revision: 2454 $" SCST_CONST_VERSION
|
||||
|
||||
#ifndef aligned_u64
|
||||
#define aligned_u64 uint64_t __attribute__((aligned(8)))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Due to variable size, this structure must always be last in any outer
|
||||
* structure!
|
||||
*/
|
||||
struct scst_event {
|
||||
int32_t payload_len;
|
||||
|
||||
uint32_t event_code;
|
||||
|
||||
uint32_t event_id; /* ID uniquely identifying this event */
|
||||
|
||||
/*
|
||||
* Event's issuer's name, i.e. SCST name of the corresponding module
|
||||
* (target driver or dev handler). SCST_EVENT_SCST_CORE_ISSUER for SCST
|
||||
* core.
|
||||
*/
|
||||
char issuer_name[SCST_MAX_NAME];
|
||||
|
||||
uint8_t payload[]; /* event's payload */
|
||||
};
|
||||
|
||||
#ifdef __KERNEL__
|
||||
typedef void (*scst_event_done_notify_fn) (struct scst_event *event,
|
||||
void *priv, int status);
|
||||
|
||||
struct scst_event_entry {
|
||||
struct list_head events_list_entry;
|
||||
scst_event_done_notify_fn event_notify_fn;
|
||||
void *notify_fn_priv;
|
||||
unsigned long event_timeout; /* in jiffies */
|
||||
int *pqueued_events_cnt;
|
||||
union {
|
||||
struct work_struct scst_event_queue_work;
|
||||
struct delayed_work event_timeout_work;
|
||||
};
|
||||
|
||||
struct scst_event event;
|
||||
/* event's payload */
|
||||
};
|
||||
#endif
|
||||
|
||||
struct scst_event_user {
|
||||
int32_t max_event_size;
|
||||
struct scst_event out_event;
|
||||
};
|
||||
|
||||
struct scst_event_notify_done {
|
||||
uint32_t event_id;
|
||||
int status;
|
||||
};
|
||||
|
||||
/* IOCTLs */
|
||||
#define SCST_EVENT_ALLOW_EVENT _IOW('u', 1, struct scst_event)
|
||||
#define SCST_EVENT_DISALLOW_EVENT _IOW('u', 2, struct scst_event)
|
||||
#define SCST_EVENT_GET_NEXT_EVENT _IOWR('u', 3, struct scst_event_user)
|
||||
#define SCST_EVENT_NOTIFY_DONE _IOW('u', 4, struct scst_event_notify_done)
|
||||
|
||||
#ifdef __KERNEL__
|
||||
void scst_event_queue(uint32_t event_code, const char *issuer_name,
|
||||
struct scst_event_entry *e);
|
||||
#endif
|
||||
|
||||
/*************************************************************
|
||||
** SCST events
|
||||
*************************************************************/
|
||||
|
||||
/* SCST core issuer name */
|
||||
#define SCST_EVENT_SCST_CORE_ISSUER "SCST core"
|
||||
|
||||
/** SCST core's events **/
|
||||
|
||||
#define SCST_EVENT_LUN_NOT_FOUND 1
|
||||
struct scst_event_lun_not_found_payload {
|
||||
aligned_u64 lun;
|
||||
uint8_t initiator_name[SCST_MAX_EXTERNAL_NAME];
|
||||
uint8_t target_name[SCST_MAX_EXTERNAL_NAME];
|
||||
};
|
||||
|
||||
#define SCST_EVENT_NEGATIVE_LUNS_INQUIRY 2
|
||||
struct scst_event_negative_luns_inquiry_payload {
|
||||
uint8_t initiator_name[SCST_MAX_EXTERNAL_NAME];
|
||||
uint8_t target_name[SCST_MAX_EXTERNAL_NAME];
|
||||
};
|
||||
|
||||
#define SCST_EVENT_EXT_BLOCKING_DONE 3
|
||||
struct scst_event_ext_blocking_done_payload {
|
||||
uint8_t device_name[SCST_MAX_EXTERNAL_NAME];
|
||||
uint32_t data_len;
|
||||
uint8_t data[];
|
||||
};
|
||||
|
||||
#define SCST_EVENT_TM_FN_RECEIVED 4
|
||||
struct scst_event_tm_fn_received_payload {
|
||||
uint32_t fn; /* TM fn */
|
||||
aligned_u64 lun;
|
||||
uint8_t device_name[SCST_MAX_EXTERNAL_NAME];
|
||||
uint8_t initiator_name[SCST_MAX_EXTERNAL_NAME];
|
||||
uint8_t target_name[SCST_MAX_EXTERNAL_NAME];
|
||||
uint8_t session_sysfs_name[SCST_MAX_EXTERNAL_NAME];
|
||||
struct { /* if ABORT TASK, then tag and CDB of cmd to abort */
|
||||
aligned_u64 cmd_to_abort_tag;
|
||||
uint8_t cdb[SCST_MAX_CDB_SIZE];
|
||||
};
|
||||
};
|
||||
|
||||
#define SCST_EVENT_STPG_USER_INVOKE 5
|
||||
struct scst_event_stpg_descr {
|
||||
uint16_t group_id;
|
||||
/*
|
||||
* Better to keep below fields as small as possible to fit
|
||||
* in single page as many descriptors as possible.
|
||||
*/
|
||||
uint8_t prev_state[32];
|
||||
uint8_t new_state[32];
|
||||
uint8_t dg_name[64];
|
||||
uint8_t tg_name[64];
|
||||
};
|
||||
struct scst_event_stpg_payload {
|
||||
aligned_u64 stpg_cmd_tag;
|
||||
uint8_t device_name[64];
|
||||
uint16_t stpg_descriptors_cnt;
|
||||
struct scst_event_stpg_descr stpg_descriptors[0];
|
||||
};
|
||||
|
||||
#endif /* __SCST_EVENT_H */
|
||||
@@ -71,10 +71,11 @@
|
||||
#define UCMD_STATE_BUF_ALLOCING 2
|
||||
#define UCMD_STATE_EXECING 3
|
||||
#define UCMD_STATE_ON_FREEING 4
|
||||
#define UCMD_STATE_ON_FREE_SKIPPED 5
|
||||
#define UCMD_STATE_ON_CACHE_FREEING 6
|
||||
#define UCMD_STATE_TM_RECEIVED_EXECING 7
|
||||
#define UCMD_STATE_TM_DONE_EXECING 8
|
||||
#define UCMD_STATE_ON_CACHE_FREEING 5
|
||||
#define UCMD_STATE_EXT_COPY_REMAPPING 6
|
||||
#define UCMD_STATE_ON_FREE_SKIPPED 7
|
||||
#define UCMD_STATE_TM_RECEIVED_EXECING 8
|
||||
#define UCMD_STATE_TM_DONE_EXECING 9
|
||||
|
||||
#define UCMD_STATE_ATTACH_SESS 0x20
|
||||
#define UCMD_STATE_DETACH_SESS 0x21
|
||||
@@ -96,6 +97,8 @@ struct scst_user_opt {
|
||||
uint8_t d_sense;
|
||||
|
||||
uint8_t has_own_order_mgmt;
|
||||
|
||||
uint8_t ext_copy_remap_supported;
|
||||
};
|
||||
|
||||
struct scst_user_dev_desc {
|
||||
@@ -212,6 +215,19 @@ struct scst_user_tm {
|
||||
uint8_t cmd_sn_set;
|
||||
};
|
||||
|
||||
struct scst_user_ext_copy_data_descr {
|
||||
aligned_u64 src_lba;
|
||||
aligned_u64 dst_lba;
|
||||
int32_t data_len; /* in bytes */
|
||||
};
|
||||
|
||||
struct scst_user_ext_copy_remap {
|
||||
aligned_u64 sess_h;
|
||||
aligned_u64 src_sess_h;
|
||||
aligned_u64 dst_sess_h;
|
||||
struct scst_user_ext_copy_data_descr data_descr;
|
||||
};
|
||||
|
||||
struct scst_user_get_cmd {
|
||||
uint32_t cmd_h;
|
||||
uint32_t subcode;
|
||||
@@ -224,6 +240,7 @@ struct scst_user_get_cmd {
|
||||
struct scst_user_scsi_on_free_cmd on_free_cmd;
|
||||
struct scst_user_on_cached_mem_free on_cached_mem_free;
|
||||
struct scst_user_tm tm_cmd;
|
||||
struct scst_user_ext_copy_remap remap_cmd;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -253,6 +270,15 @@ struct scst_user_scsi_cmd_reply_alloc_mem {
|
||||
aligned_u64 pbuf;
|
||||
};
|
||||
|
||||
/*
|
||||
* Same as struct scst_data_descriptor, but suitable to pass kernel/user
|
||||
* space boundary
|
||||
*/
|
||||
struct scst_user_data_descriptor {
|
||||
aligned_u64 usdd_lba;
|
||||
aligned_u64 usdd_blocks;
|
||||
};
|
||||
|
||||
/* Be careful adding new members here, this structure is allocated on stack! */
|
||||
struct scst_user_scsi_cmd_reply_exec {
|
||||
int32_t resp_data_len;
|
||||
@@ -260,8 +286,26 @@ struct scst_user_scsi_cmd_reply_exec {
|
||||
|
||||
#define SCST_EXEC_REPLY_BACKGROUND 0
|
||||
#define SCST_EXEC_REPLY_COMPLETED 1
|
||||
#define SCST_EXEC_REPLY_DO_WRITE_SAME 2
|
||||
uint8_t reply_type;
|
||||
|
||||
uint8_t status;
|
||||
union {
|
||||
struct {
|
||||
uint8_t sense_len;
|
||||
aligned_u64 psense_buffer;
|
||||
};
|
||||
struct {
|
||||
uint16_t ws_descriptors_len;
|
||||
aligned_u64 ws_descriptors;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/* Be careful adding new members here, this structure is allocated on stack! */
|
||||
struct scst_user_ext_copy_reply_remap {
|
||||
aligned_u64 remap_descriptors;
|
||||
uint16_t remap_descriptors_len;
|
||||
uint8_t status;
|
||||
uint8_t sense_len;
|
||||
aligned_u64 psense_buffer;
|
||||
@@ -276,6 +320,7 @@ struct scst_user_reply_cmd {
|
||||
struct scst_user_scsi_cmd_reply_parse parse_reply;
|
||||
struct scst_user_scsi_cmd_reply_alloc_mem alloc_reply;
|
||||
struct scst_user_scsi_cmd_reply_exec exec_reply;
|
||||
struct scst_user_ext_copy_reply_remap remap_reply;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -344,5 +389,7 @@ struct scst_user_get_multi {
|
||||
_IOWR('s', UCMD_STATE_TM_RECEIVED_EXECING, struct scst_user_tm)
|
||||
#define SCST_USER_TASK_MGMT_DONE \
|
||||
_IOWR('s', UCMD_STATE_TM_DONE_EXECING, struct scst_user_tm)
|
||||
#define SCST_USER_EXT_COPY_REMAP \
|
||||
_IOWR('s', UCMD_STATE_EXT_COPY_REMAPPING, struct scst_user_ext_copy_remap)
|
||||
|
||||
#endif /* __SCST_USER_H */
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
diff --git a/drivers/Kconfig b/drivers/Kconfig
|
||||
index c70d6e4..0a4ed1b 100644
|
||||
--- a/drivers/Kconfig
|
||||
+++ b/drivers/Kconfig
|
||||
@@ -26,6 +26,8 @@ source "drivers/ide/Kconfig"
|
||||
|
||||
source "drivers/scsi/Kconfig"
|
||||
|
||||
+source "drivers/scst/Kconfig"
|
||||
+
|
||||
source "drivers/ata/Kconfig"
|
||||
|
||||
source "drivers/md/Kconfig"
|
||||
@@ -0,0 +1,14 @@
|
||||
ccflags-y += -Wno-unused-parameter
|
||||
|
||||
obj-m := scst_cdrom.o scst_changer.o scst_disk.o scst_modisk.o scst_tape.o \
|
||||
scst_vdisk.o scst_raid.o scst_processor.o scst_user.o
|
||||
|
||||
obj-$(CONFIG_SCST_DISK) += scst_disk.o
|
||||
obj-$(CONFIG_SCST_TAPE) += scst_tape.o
|
||||
obj-$(CONFIG_SCST_CDROM) += scst_cdrom.o
|
||||
obj-$(CONFIG_SCST_MODISK) += scst_modisk.o
|
||||
obj-$(CONFIG_SCST_CHANGER) += scst_changer.o
|
||||
obj-$(CONFIG_SCST_RAID) += scst_raid.o
|
||||
obj-$(CONFIG_SCST_PROCESSOR) += scst_processor.o
|
||||
obj-$(CONFIG_SCST_VDISK) += scst_vdisk.o
|
||||
obj-$(CONFIG_SCST_USER) += scst_user.o
|
||||
@@ -0,0 +1,12 @@
|
||||
diff --git a/drivers/Makefile b/drivers/Makefile
|
||||
index 527a6da..db2c24f 100644
|
||||
--- a/drivers/Makefile
|
||||
+++ b/drivers/Makefile
|
||||
@@ -137,6 +137,7 @@ obj-$(CONFIG_SSB) += ssb/
|
||||
obj-$(CONFIG_BCMA) += bcma/
|
||||
obj-$(CONFIG_VHOST_RING) += vhost/
|
||||
obj-$(CONFIG_VLYNQ) += vlynq/
|
||||
+obj-$(CONFIG_SCST) += scst/
|
||||
obj-$(CONFIG_STAGING) += staging/
|
||||
obj-y += platform/
|
||||
#common clk code
|
||||
@@ -0,0 +1,13 @@
|
||||
ccflags-y += -Wno-unused-parameter
|
||||
|
||||
scst-y += scst_main.o
|
||||
scst-y += scst_pres.o
|
||||
scst-y += scst_targ.o
|
||||
scst-y += scst_lib.o
|
||||
scst-y += scst_sysfs.o
|
||||
scst-y += scst_mem.o
|
||||
scst-y += scst_tg.o
|
||||
scst-y += scst_debug.o
|
||||
|
||||
obj-$(CONFIG_SCST) += scst.o dev_handlers/ fcst/ iscsi-scst/ qla2xxx-target/ \
|
||||
srpt/ scst_local/
|
||||
@@ -56,6 +56,8 @@ ifdef CONFIG_DLM_MODULE
|
||||
scst-y += scst_dlm.o
|
||||
endif
|
||||
scst-y += scst_tg.o
|
||||
scst-y += scst_event.o
|
||||
scst-y += scst_copy_mgr.o
|
||||
obj-$(CONFIG_SCST) += scst.o dev_handlers/
|
||||
|
||||
obj-$(BUILD_DEV) += $(DEV_HANDLERS_DIR)/
|
||||
|
||||
@@ -92,6 +92,8 @@ EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECKS
|
||||
#EXTRA_CFLAGS += -DCONFIG_SCST_TRACING
|
||||
EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions
|
||||
|
||||
#EXTRA_CFLAGS += -DCONFIG_DEBUG_EXT_COPY_REMAP
|
||||
|
||||
clean:
|
||||
rm -f *.o *.ko .*.cmd *.mod.c .*.d .depend Modules.symvers \
|
||||
Module.symvers Module.markers modules.order
|
||||
|
||||
@@ -123,6 +123,7 @@ static int cdrom_attach(struct scst_device *dev)
|
||||
|
||||
if (rc == 0) {
|
||||
uint32_t sector_size = get_unaligned_be32(&buffer[4]);
|
||||
|
||||
if (sector_size == 0)
|
||||
dev->block_shift = CDROM_DEF_BLOCK_SHIFT;
|
||||
else
|
||||
|
||||
@@ -217,6 +217,7 @@ static int disk_attach(struct scst_device *dev)
|
||||
}
|
||||
if (rc == 0) {
|
||||
uint32_t sector_size = get_unaligned_be32(&buffer[4]);
|
||||
|
||||
if (sector_size == 0)
|
||||
dev->block_shift = DISK_DEF_BLOCK_SHIFT;
|
||||
else
|
||||
@@ -476,6 +477,7 @@ split:
|
||||
|
||||
if (unlikely(sg_is_chain(&sg[j]))) {
|
||||
bool reset_start_sg = (start_sg == &sg[j]);
|
||||
|
||||
sg = sg_chain_ptr(&sg[j]);
|
||||
j = 0;
|
||||
if (reset_start_sg)
|
||||
|
||||
@@ -222,6 +222,7 @@ static int modisk_attach(struct scst_device *dev)
|
||||
|
||||
if (rc == 0) {
|
||||
uint32_t sector_size = get_unaligned_be32(&buffer[4]);
|
||||
|
||||
if (sector_size == 0)
|
||||
dev->block_shift = MODISK_DEF_BLOCK_SHIFT;
|
||||
else
|
||||
|
||||
@@ -206,6 +206,7 @@ static int tape_attach(struct scst_device *dev)
|
||||
|
||||
if (rc == 0) {
|
||||
int medium_type, mode, speed, density;
|
||||
|
||||
if (buffer[3] == 8)
|
||||
dev->block_size = get_unaligned_be24(&buffer[9]);
|
||||
else
|
||||
@@ -299,6 +300,7 @@ static int tape_done(struct scst_cmd *cmd)
|
||||
(cmd->sense[2] & 0xe0)) {
|
||||
/* EOF, EOM, or ILI */
|
||||
unsigned int TransferLength, Residue = 0;
|
||||
|
||||
if ((cmd->sense[2] & 0x0f) == BLANK_CHECK)
|
||||
/* No need for EOM in this case */
|
||||
cmd->sense[2] &= 0xcf;
|
||||
@@ -312,6 +314,7 @@ static int tape_done(struct scst_cmd *cmd)
|
||||
cmd->cdb[1], TransferLength, Residue);
|
||||
if (TransferLength > Residue) {
|
||||
int resp_data_len = TransferLength - Residue;
|
||||
|
||||
if (cmd->cdb[1] & 1) {
|
||||
/*
|
||||
* No need for locks here, since
|
||||
|
||||
@@ -70,6 +70,7 @@ struct scst_user_dev {
|
||||
unsigned int swp:1;
|
||||
unsigned int d_sense:1;
|
||||
unsigned int has_own_order_mgmt:1;
|
||||
unsigned int ext_copy_remap_supported:1;
|
||||
|
||||
int (*generic_parse)(struct scst_cmd *cmd);
|
||||
|
||||
@@ -260,6 +261,7 @@ static inline bool ucmd_get_check(struct scst_user_cmd *ucmd)
|
||||
{
|
||||
int r = atomic_inc_return(&ucmd->ucmd_ref);
|
||||
int res;
|
||||
|
||||
if (unlikely(r == 1)) {
|
||||
TRACE_DBG("ucmd %p is being destroyed", ucmd);
|
||||
atomic_dec(&ucmd->ucmd_ref);
|
||||
@@ -550,6 +552,7 @@ static int dev_user_alloc_sg(struct scst_user_cmd *ucmd, int cached_buff)
|
||||
} else {
|
||||
/* Make out_sg->offset 0 */
|
||||
int len = cmd->bufflen + ucmd->first_page_offset;
|
||||
|
||||
out_sg_pages = (len >> PAGE_SHIFT) + ((len & ~PAGE_MASK) != 0);
|
||||
orig_bufflen = (out_sg_pages << PAGE_SHIFT) + cmd->out_bufflen;
|
||||
pool = dev->pool;
|
||||
@@ -611,6 +614,7 @@ static int dev_user_alloc_sg(struct scst_user_cmd *ucmd, int cached_buff)
|
||||
|
||||
if (unlikely(cmd->sg_cnt > cmd->tgt_dev->max_sg_cnt)) {
|
||||
static int ll;
|
||||
|
||||
if ((ll < 10) || TRACING_MINOR()) {
|
||||
PRINT_INFO("Unable to complete command due to "
|
||||
"SG IO count limitation (requested %d, "
|
||||
@@ -949,6 +953,37 @@ static int dev_user_exec(struct scst_cmd *cmd)
|
||||
return res;
|
||||
}
|
||||
|
||||
static void dev_user_ext_copy_remap(struct scst_cmd *cmd,
|
||||
struct scst_ext_copy_seg_descr *seg)
|
||||
{
|
||||
struct scst_user_cmd *ucmd = cmd->dh_priv;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_DBG("Preparing EXT_COPY_REMAP for user space (ucmd=%p, h=%d, "
|
||||
"seg %p)", ucmd, ucmd->h, seg);
|
||||
|
||||
ucmd->user_cmd_payload_len =
|
||||
offsetof(struct scst_user_get_cmd, remap_cmd) +
|
||||
sizeof(ucmd->user_cmd.remap_cmd);
|
||||
ucmd->user_cmd.cmd_h = ucmd->h;
|
||||
ucmd->user_cmd.subcode = SCST_USER_EXT_COPY_REMAP;
|
||||
ucmd->user_cmd.remap_cmd.sess_h = (unsigned long)cmd->tgt_dev;
|
||||
|
||||
ucmd->user_cmd.remap_cmd.src_sess_h = (unsigned long)seg->src_tgt_dev;
|
||||
ucmd->user_cmd.remap_cmd.dst_sess_h = (unsigned long)seg->dst_tgt_dev;
|
||||
ucmd->user_cmd.remap_cmd.data_descr.src_lba = seg->data_descr.src_lba;
|
||||
ucmd->user_cmd.remap_cmd.data_descr.dst_lba = seg->data_descr.dst_lba;
|
||||
ucmd->user_cmd.remap_cmd.data_descr.data_len = seg->data_descr.data_len;
|
||||
|
||||
ucmd->state = UCMD_STATE_EXT_COPY_REMAPPING;
|
||||
|
||||
dev_user_add_to_ready(ucmd);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
static void dev_user_free_sgv(struct scst_user_cmd *ucmd)
|
||||
{
|
||||
if (ucmd->sgv != NULL) {
|
||||
@@ -1058,6 +1093,7 @@ static void dev_user_set_block_size(struct scst_cmd *cmd, int block_size)
|
||||
dev->block_size = block_size;
|
||||
else {
|
||||
struct scst_user_dev *udev = cmd->dev->dh_priv;
|
||||
|
||||
dev->block_size = udev->def_block_size;
|
||||
}
|
||||
dev->block_shift = -1; /* not used */
|
||||
@@ -1180,7 +1216,8 @@ static void dev_user_add_to_ready(struct scst_user_cmd *ucmd)
|
||||
&dev->ready_cmd_list);
|
||||
}
|
||||
do_wake |= ((ucmd->state == UCMD_STATE_ON_CACHE_FREEING) ||
|
||||
(ucmd->state == UCMD_STATE_ON_FREEING));
|
||||
(ucmd->state == UCMD_STATE_ON_FREEING) ||
|
||||
(ucmd->state == UCMD_STATE_EXT_COPY_REMAPPING));
|
||||
}
|
||||
|
||||
if (do_wake) {
|
||||
@@ -1210,8 +1247,9 @@ static int dev_user_map_buf(struct scst_user_cmd *ucmd, unsigned long ubuff,
|
||||
|
||||
ucmd->num_data_pages = num_pg;
|
||||
|
||||
ucmd->data_pages = kmalloc(sizeof(*ucmd->data_pages) * ucmd->num_data_pages,
|
||||
GFP_KERNEL);
|
||||
ucmd->data_pages = kmalloc_array(ucmd->num_data_pages,
|
||||
sizeof(*ucmd->data_pages),
|
||||
GFP_KERNEL);
|
||||
if (ucmd->data_pages == NULL) {
|
||||
TRACE(TRACE_OUT_OF_MEM, "Unable to allocate data_pages array "
|
||||
"(num_data_pages=%d)", ucmd->num_data_pages);
|
||||
@@ -1278,6 +1316,7 @@ static int dev_user_process_reply_alloc(struct scst_user_cmd *ucmd,
|
||||
|
||||
if (likely(reply->alloc_reply.pbuf != 0)) {
|
||||
int pages;
|
||||
|
||||
if (ucmd->buff_cached) {
|
||||
if (unlikely((reply->alloc_reply.pbuf & ~PAGE_MASK) != 0)) {
|
||||
PRINT_ERROR("Supplied pbuf %llx isn't "
|
||||
@@ -1444,6 +1483,254 @@ static int dev_user_process_reply_on_cache_free(struct scst_user_cmd *ucmd)
|
||||
return res;
|
||||
}
|
||||
|
||||
static int dev_user_process_reply_ext_copy_remap(struct scst_user_cmd *ucmd,
|
||||
struct scst_user_reply_cmd *reply)
|
||||
{
|
||||
int res = 0, rc, count = 0, i, len;
|
||||
struct scst_user_ext_copy_reply_remap *rreply = &reply->remap_reply;
|
||||
struct scst_cmd *cmd = ucmd->cmd;
|
||||
uint8_t *buf;
|
||||
struct scst_user_ext_copy_data_descr *uleft;
|
||||
struct scst_ext_copy_data_descr *left = NULL;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (unlikely((rreply->status != 0) || (rreply->sense_len != 0)))
|
||||
goto out_status;
|
||||
|
||||
if (rreply->remap_descriptors_len == 0) {
|
||||
TRACE_DBG("No remap leftovers (ucmd %p, cmd %p)", ucmd, cmd);
|
||||
goto out_done;
|
||||
}
|
||||
|
||||
if (unlikely(rreply->remap_descriptors_len > PAGE_SIZE)) {
|
||||
PRINT_ERROR("Too many leftover REMAP descriptors (len %d, ucmd %p, "
|
||||
"cmd %p)", rreply->remap_descriptors_len, ucmd, cmd);
|
||||
res = -EOVERFLOW;
|
||||
goto out_hw_err;
|
||||
}
|
||||
|
||||
buf = kzalloc(rreply->remap_descriptors_len, GFP_KERNEL);
|
||||
if (unlikely(buf == NULL)) {
|
||||
PRINT_ERROR("Unable to alloc leftover remap descriptors buf "
|
||||
"(size %d)", rreply->remap_descriptors_len);
|
||||
goto out_busy;
|
||||
}
|
||||
|
||||
rc = copy_from_user(buf,
|
||||
(void __user *)(unsigned long)rreply->remap_descriptors,
|
||||
rreply->remap_descriptors_len);
|
||||
if (unlikely(rc != 0)) {
|
||||
PRINT_ERROR("Failed to copy %d leftover remap descriptors' "
|
||||
"bytes", rc);
|
||||
res = -EFAULT;
|
||||
goto out_free_buf;
|
||||
}
|
||||
|
||||
uleft = (struct scst_user_ext_copy_data_descr *)buf;
|
||||
count = rreply->remap_descriptors_len / sizeof(*uleft);
|
||||
if (unlikely((rreply->remap_descriptors_len % sizeof(*uleft)) != 0)) {
|
||||
PRINT_ERROR("Invalid leftover remap descriptors (ucmd %p, "
|
||||
"cmd %p, count %d)", ucmd, cmd, count);
|
||||
res = -EINVAL;
|
||||
goto out_hw_err_free_buf;
|
||||
}
|
||||
|
||||
left = kmalloc_array(count, sizeof(*left), GFP_KERNEL);
|
||||
if (unlikely(left == NULL)) {
|
||||
PRINT_ERROR("Unable to alloc leftover remap descriptors "
|
||||
"(size %zd, count %d)", sizeof(*left) * count, count);
|
||||
goto out_busy_free_buf;
|
||||
}
|
||||
|
||||
TRACE_DBG("count %d", count);
|
||||
|
||||
len = 0;
|
||||
for (i = 0; i < count; i++) {
|
||||
TRACE_DBG("src_lba %lld, dst_lba %lld, data_len %d (len %d)",
|
||||
(long long)uleft[i].src_lba, (long long)uleft[i].dst_lba,
|
||||
uleft[i].data_len, len);
|
||||
if (unlikely(uleft[i].data_len == 0)) {
|
||||
PRINT_ERROR("Invalid data descr %d len %d (cmd %p)", i,
|
||||
uleft[i].data_len, cmd);
|
||||
res = -EINVAL;
|
||||
goto out_hw_err_free_buf;
|
||||
}
|
||||
left[i].src_lba = uleft[i].src_lba;
|
||||
left[i].dst_lba = uleft[i].dst_lba;
|
||||
left[i].data_len = uleft[i].data_len;
|
||||
len += uleft[i].data_len;
|
||||
}
|
||||
|
||||
if (unlikely(len > scst_ext_copy_get_cur_seg_data_len(cmd))) {
|
||||
PRINT_ERROR("Invalid data descr len %d (cmd %p, seg descr len %d)",
|
||||
len, cmd, scst_ext_copy_get_cur_seg_data_len(cmd));
|
||||
res = -EINVAL;
|
||||
goto out_hw_err_free_buf;
|
||||
}
|
||||
|
||||
out_free_buf:
|
||||
kfree(buf);
|
||||
|
||||
out_done:
|
||||
if (unlikely(res != 0)) {
|
||||
kfree(left);
|
||||
left = NULL;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
scst_ext_copy_remap_done(cmd, left, count);
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
out_hw_err:
|
||||
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
|
||||
goto out_done;
|
||||
|
||||
out_hw_err_free_buf:
|
||||
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
|
||||
goto out_free_buf;
|
||||
|
||||
out_busy_free_buf:
|
||||
scst_set_busy(cmd);
|
||||
goto out_free_buf;
|
||||
|
||||
out_busy:
|
||||
scst_set_busy(cmd);
|
||||
goto out_done;
|
||||
|
||||
out_status:
|
||||
TRACE_DBG("Remap finished with status %d (ucmd %p, cmd %p)",
|
||||
rreply->status, ucmd, cmd);
|
||||
|
||||
if (rreply->sense_len != 0) {
|
||||
int sense_len;
|
||||
|
||||
res = scst_alloc_sense(cmd, 0);
|
||||
if (res != 0)
|
||||
goto out_hw_err;
|
||||
|
||||
sense_len = min_t(int, cmd->sense_buflen, rreply->sense_len);
|
||||
|
||||
rc = copy_from_user(cmd->sense,
|
||||
(void __user *)(unsigned long)rreply->psense_buffer,
|
||||
sense_len);
|
||||
if (rc != 0) {
|
||||
PRINT_ERROR("Failed to copy %d sense's bytes", rc);
|
||||
res = -EFAULT;
|
||||
goto out_hw_err;
|
||||
}
|
||||
cmd->sense_valid_len = sense_len;
|
||||
}
|
||||
scst_set_cmd_error_status(cmd, rreply->status);
|
||||
goto out_done;
|
||||
}
|
||||
|
||||
static int dev_user_process_ws_reply(struct scst_user_cmd *ucmd,
|
||||
struct scst_user_scsi_cmd_reply_exec *ereply)
|
||||
{
|
||||
int res = 0, rc, count, i;
|
||||
struct scst_cmd *cmd = ucmd->cmd;
|
||||
uint8_t *buf;
|
||||
struct scst_user_data_descriptor *uwhere;
|
||||
struct scst_data_descriptor *where;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (unlikely(cmd->cdb[0] != WRITE_SAME) &&
|
||||
unlikely(cmd->cdb[0] != WRITE_SAME_16)) {
|
||||
PRINT_ERROR("Request to process WRITE SAME for not WRITE SAME "
|
||||
"CDB (ucmd %p, cmd %p, op %s)", ucmd, cmd,
|
||||
scst_get_opcode_name(cmd));
|
||||
res = -EINVAL;
|
||||
goto out_hw_err;
|
||||
}
|
||||
|
||||
if (unlikely(ereply->status != 0)) {
|
||||
PRINT_ERROR("Request to process WRITE SAME with not 0 status "
|
||||
"(ucmd %p, cmd %p, status %d)", ucmd, cmd, ereply->status);
|
||||
res = -EINVAL;
|
||||
goto out_hw_err;
|
||||
}
|
||||
|
||||
if (ereply->ws_descriptors_len == 0) {
|
||||
scst_write_same(cmd, NULL);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (unlikely(ereply->ws_descriptors_len > PAGE_SIZE)) {
|
||||
PRINT_ERROR("Too many WRITE SAME descriptors (len %d, ucmd %p, "
|
||||
"cmd %p)", ereply->ws_descriptors_len, ucmd, cmd);
|
||||
res = -EOVERFLOW;
|
||||
goto out_hw_err;
|
||||
}
|
||||
|
||||
buf = kzalloc(ereply->ws_descriptors_len, GFP_KERNEL);
|
||||
if (unlikely(buf == NULL)) {
|
||||
PRINT_ERROR("Unable to alloc WS descriptors buf (size %d)",
|
||||
ereply->ws_descriptors_len);
|
||||
goto out_busy;
|
||||
}
|
||||
|
||||
rc = copy_from_user(buf,
|
||||
(void __user *)(unsigned long)ereply->ws_descriptors,
|
||||
ereply->ws_descriptors_len);
|
||||
if (unlikely(rc != 0)) {
|
||||
PRINT_ERROR("Failed to copy %d WS descriptors' bytes", rc);
|
||||
res = -EFAULT;
|
||||
goto out_free_buf;
|
||||
}
|
||||
|
||||
uwhere = (struct scst_user_data_descriptor *)buf;
|
||||
count = ereply->ws_descriptors_len / sizeof(*uwhere);
|
||||
if (unlikely((ereply->ws_descriptors_len % sizeof(*uwhere)) != 0) ||
|
||||
unlikely(uwhere[count-1].usdd_blocks != 0)) {
|
||||
PRINT_ERROR("Invalid WS descriptors (ucmd %p, cmd %p)",
|
||||
ucmd, cmd);
|
||||
res = -EINVAL;
|
||||
goto out_free_buf;
|
||||
}
|
||||
|
||||
where = kmalloc_array(count, sizeof(*where), GFP_KERNEL);
|
||||
if (unlikely(where == NULL)) {
|
||||
PRINT_ERROR("Unable to alloc WS descriptors where (size %zd)",
|
||||
sizeof(*where) * count);
|
||||
goto out_busy_free_buf;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
where[i].sdd_lba = uwhere[i].usdd_lba;
|
||||
where[i].sdd_blocks = uwhere[i].usdd_blocks;
|
||||
}
|
||||
kfree(buf);
|
||||
|
||||
scst_write_same(cmd, where);
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
out_free_buf:
|
||||
kfree(buf);
|
||||
|
||||
out_hw_err:
|
||||
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
|
||||
|
||||
out_compl:
|
||||
cmd->completed = 1;
|
||||
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_DIRECT);
|
||||
/* !! At this point cmd can be already freed !! */
|
||||
goto out;
|
||||
|
||||
out_busy_free_buf:
|
||||
kfree(buf);
|
||||
|
||||
out_busy:
|
||||
scst_set_busy(cmd);
|
||||
goto out_compl;
|
||||
}
|
||||
|
||||
static int dev_user_process_reply_exec(struct scst_user_cmd *ucmd,
|
||||
struct scst_user_reply_cmd *reply)
|
||||
{
|
||||
@@ -1480,6 +1767,9 @@ static int dev_user_process_reply_exec(struct scst_user_cmd *ucmd,
|
||||
ucmd->background_exec = 1;
|
||||
TRACE_DBG("Background ucmd %p", ucmd);
|
||||
goto out_compl;
|
||||
} else if (ereply->reply_type == SCST_EXEC_REPLY_DO_WRITE_SAME) {
|
||||
res = dev_user_process_ws_reply(ucmd, ereply);
|
||||
goto out;
|
||||
} else
|
||||
goto out_inval;
|
||||
|
||||
@@ -1491,6 +1781,7 @@ static int dev_user_process_reply_exec(struct scst_user_cmd *ucmd,
|
||||
if (ereply->resp_data_len != 0) {
|
||||
if (ucmd->ubuff == 0) {
|
||||
int pages, rc;
|
||||
|
||||
if (unlikely(ereply->pbuf == 0))
|
||||
goto out_busy;
|
||||
if (ucmd->buff_cached) {
|
||||
@@ -1657,6 +1948,10 @@ unlock_process:
|
||||
res = dev_user_process_reply_on_cache_free(ucmd);
|
||||
break;
|
||||
|
||||
case UCMD_STATE_EXT_COPY_REMAPPING:
|
||||
res = dev_user_process_reply_ext_copy_remap(ucmd, reply);
|
||||
break;
|
||||
|
||||
case UCMD_STATE_TM_RECEIVED_EXECING:
|
||||
case UCMD_STATE_TM_DONE_EXECING:
|
||||
res = dev_user_process_reply_tm_exec(ucmd,
|
||||
@@ -2036,6 +2331,7 @@ static int dev_user_reply_get_cmd(struct file *file, void __user *arg)
|
||||
|
||||
if (ureply != 0) {
|
||||
unsigned long u = (unsigned long)ureply;
|
||||
|
||||
rc = copy_from_user(&reply, (void __user *)u, sizeof(reply));
|
||||
if (unlikely(rc != 0)) {
|
||||
PRINT_ERROR("Failed to copy %d user's bytes", rc);
|
||||
@@ -2188,6 +2484,7 @@ static long dev_user_ioctl(struct file *file, unsigned int cmd,
|
||||
case SCST_USER_REGISTER_DEVICE:
|
||||
{
|
||||
struct scst_user_dev_desc *dev_desc;
|
||||
|
||||
TRACE_DBG("%s", "REGISTER_DEVICE");
|
||||
dev_desc = kmalloc(sizeof(*dev_desc), GFP_KERNEL);
|
||||
if (dev_desc == NULL) {
|
||||
@@ -2223,6 +2520,7 @@ static long dev_user_ioctl(struct file *file, unsigned int cmd,
|
||||
case SCST_USER_SET_OPTIONS:
|
||||
{
|
||||
struct scst_user_opt opt;
|
||||
|
||||
TRACE_DBG("%s", "SET_OPTIONS");
|
||||
rc = copy_from_user(&opt, (void __user *)arg, sizeof(opt));
|
||||
if (rc != 0) {
|
||||
@@ -2353,6 +2651,7 @@ static void dev_user_unjam_cmd(struct scst_user_cmd *ucmd, int busy,
|
||||
break;
|
||||
|
||||
case UCMD_STATE_EXECING:
|
||||
case UCMD_STATE_EXT_COPY_REMAPPING:
|
||||
if (flags != NULL)
|
||||
spin_unlock_irqrestore(&dev->udev_cmd_threads.cmd_list_lock,
|
||||
*flags);
|
||||
@@ -2371,8 +2670,13 @@ static void dev_user_unjam_cmd(struct scst_user_cmd *ucmd, int busy,
|
||||
SCST_LOAD_SENSE(scst_sense_lun_not_supported));
|
||||
}
|
||||
|
||||
ucmd->cmd->scst_cmd_done(ucmd->cmd, SCST_CMD_STATE_DEFAULT,
|
||||
SCST_CONTEXT_THREAD);
|
||||
if (state == UCMD_STATE_EXECING)
|
||||
ucmd->cmd->scst_cmd_done(ucmd->cmd, SCST_CMD_STATE_DEFAULT,
|
||||
SCST_CONTEXT_THREAD);
|
||||
else {
|
||||
sBUG_ON(state != UCMD_STATE_EXT_COPY_REMAPPING);
|
||||
scst_ext_copy_remap_done(ucmd->cmd, NULL, 0);
|
||||
}
|
||||
/* !! At this point cmd and ucmd can be already freed !! */
|
||||
|
||||
if (flags != NULL)
|
||||
@@ -2911,6 +3215,11 @@ static void dev_user_setup_functions(struct scst_user_dev *dev)
|
||||
dev->devtype.dev_alloc_data_buf = dev_user_alloc_data_buf;
|
||||
dev->devtype.dev_done = NULL;
|
||||
|
||||
if (dev->ext_copy_remap_supported)
|
||||
dev->devtype.ext_copy_remap = dev_user_ext_copy_remap;
|
||||
else
|
||||
dev->devtype.ext_copy_remap = NULL;
|
||||
|
||||
if (dev->parse_type != SCST_USER_PARSE_CALL) {
|
||||
switch (dev->devtype.type) {
|
||||
case TYPE_DISK:
|
||||
@@ -3364,9 +3673,10 @@ static int __dev_user_set_opt(struct scst_user_dev *dev,
|
||||
|
||||
TRACE_DBG("dev %s, parse_type %x, on_free_cmd_type %x, "
|
||||
"memory_reuse_type %x, partial_transfers_type %x, "
|
||||
"partial_len %d", dev->name, opt->parse_type,
|
||||
opt->on_free_cmd_type, opt->memory_reuse_type,
|
||||
opt->partial_transfers_type, opt->partial_len);
|
||||
"partial_len %d, opt->ext_copy_remap_supported %d",
|
||||
dev->name, opt->parse_type, opt->on_free_cmd_type,
|
||||
opt->memory_reuse_type, opt->partial_transfers_type,
|
||||
opt->partial_len, opt->ext_copy_remap_supported);
|
||||
|
||||
if (opt->parse_type > SCST_USER_MAX_PARSE_OPT ||
|
||||
opt->on_free_cmd_type > SCST_USER_MAX_ON_FREE_CMD_OPT ||
|
||||
@@ -3385,12 +3695,13 @@ static int __dev_user_set_opt(struct scst_user_dev *dev,
|
||||
((opt->qerr == SCST_QERR_2_RESERVED) ||
|
||||
(opt->qerr > SCST_QERR_3_ABORT_THIS_NEXUS_ONLY)) ||
|
||||
(opt->swp > 1) || (opt->tas > 1) || (opt->has_own_order_mgmt > 1) ||
|
||||
(opt->d_sense > 1)) {
|
||||
(opt->d_sense > 1) || (opt->ext_copy_remap_supported > 1)) {
|
||||
PRINT_ERROR("Invalid SCSI option (tst %x, tmf_only %x, "
|
||||
"queue_alg %x, qerr %x, swp %x, tas %x, d_sense %d, "
|
||||
"has_own_order_mgmt %x)",
|
||||
"has_own_order_mgmt %x, ext_copy_remap_supported %d)",
|
||||
opt->tst, opt->tmf_only, opt->queue_alg, opt->qerr,
|
||||
opt->swp, opt->tas, opt->d_sense, opt->has_own_order_mgmt);
|
||||
opt->swp, opt->tas, opt->d_sense, opt->has_own_order_mgmt,
|
||||
opt->ext_copy_remap_supported);
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
@@ -3419,6 +3730,7 @@ static int __dev_user_set_opt(struct scst_user_dev *dev,
|
||||
dev->tas = opt->tas;
|
||||
dev->d_sense = opt->d_sense;
|
||||
dev->has_own_order_mgmt = opt->has_own_order_mgmt;
|
||||
dev->ext_copy_remap_supported = opt->ext_copy_remap_supported;
|
||||
if (dev->sdev != NULL) {
|
||||
dev->sdev->tst = opt->tst;
|
||||
dev->sdev->tmf_only = opt->tmf_only;
|
||||
@@ -3475,6 +3787,7 @@ static int dev_user_get_opt(struct file *file, void __user *arg)
|
||||
if (unlikely(res != 0))
|
||||
goto out;
|
||||
|
||||
memset(&opt, 0, sizeof(opt));
|
||||
opt.parse_type = dev->parse_type;
|
||||
opt.on_free_cmd_type = dev->on_free_cmd_type;
|
||||
opt.memory_reuse_type = dev->memory_reuse_type;
|
||||
@@ -3488,12 +3801,14 @@ static int dev_user_get_opt(struct file *file, void __user *arg)
|
||||
opt.swp = dev->swp;
|
||||
opt.d_sense = dev->d_sense;
|
||||
opt.has_own_order_mgmt = dev->has_own_order_mgmt;
|
||||
opt.ext_copy_remap_supported = dev->ext_copy_remap_supported;
|
||||
|
||||
TRACE_DBG("dev %s, parse_type %x, on_free_cmd_type %x, "
|
||||
"memory_reuse_type %x, partial_transfers_type %x, "
|
||||
"partial_len %d", dev->name, opt.parse_type,
|
||||
opt.on_free_cmd_type, opt.memory_reuse_type,
|
||||
opt.partial_transfers_type, opt.partial_len);
|
||||
"partial_len %d, ext_copy_remap_supported %d", dev->name,
|
||||
opt.parse_type, opt.on_free_cmd_type, opt.memory_reuse_type,
|
||||
opt.partial_transfers_type, opt.partial_len,
|
||||
opt.ext_copy_remap_supported);
|
||||
|
||||
rc = copy_to_user(arg, &opt, sizeof(opt));
|
||||
if (unlikely(rc != 0)) {
|
||||
@@ -3563,6 +3878,7 @@ static int dev_user_exit_dev(struct scst_user_dev *dev)
|
||||
static int __dev_user_release(void *arg)
|
||||
{
|
||||
struct scst_user_dev *dev = arg;
|
||||
|
||||
dev_user_exit_dev(dev);
|
||||
kmem_cache_free(user_dev_cachep, dev);
|
||||
return 0;
|
||||
@@ -3633,6 +3949,7 @@ static int dev_user_process_cleanup(struct scst_user_dev *dev)
|
||||
#ifdef CONFIG_SCST_EXTRACHECKS
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (int)ARRAY_SIZE(dev->ucmd_hash); i++) {
|
||||
struct list_head *head = &dev->ucmd_hash[i];
|
||||
struct scst_user_cmd *ucmd2, *tmp;
|
||||
@@ -3674,6 +3991,7 @@ static ssize_t dev_user_sysfs_commands_show(struct kobject *kobj,
|
||||
for (i = 0; i < (int)ARRAY_SIZE(udev->ucmd_hash); i++) {
|
||||
struct list_head *head = &udev->ucmd_hash[i];
|
||||
struct scst_user_cmd *ucmd;
|
||||
|
||||
list_for_each_entry(ucmd, head, hash_list_entry) {
|
||||
ppos = pos;
|
||||
pos += scnprintf(&buf[pos],
|
||||
|
||||
+511
-256
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -232,6 +232,10 @@ const char *debug_transport_id_to_initiator_name(const uint8_t *transport_id)
|
||||
transport_id[8], transport_id[9],
|
||||
transport_id[10], transport_id[11]);
|
||||
break;
|
||||
case SCST_TRANSPORTID_PROTOCOLID_COPY_MGR:
|
||||
scnprintf(name_buf, SIZEOF_NAME_BUF,
|
||||
"%s", &transport_id[2]);
|
||||
break;
|
||||
default:
|
||||
scnprintf(name_buf, SIZEOF_NAME_BUF,
|
||||
"(Not known protocol ID %x)", transport_id[0] & 0x0f);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+1375
-163
File diff suppressed because it is too large
Load Diff
+47
-2
@@ -596,6 +596,7 @@ EXPORT_SYMBOL(scst_register_target);
|
||||
static inline int test_sess_list(struct scst_tgt *tgt)
|
||||
{
|
||||
int res;
|
||||
|
||||
mutex_lock(&scst_mutex);
|
||||
res = list_empty(&tgt->sysfs_sess_list);
|
||||
mutex_unlock(&scst_mutex);
|
||||
@@ -637,6 +638,7 @@ void scst_unregister_target(struct scst_tgt *tgt)
|
||||
again:
|
||||
{
|
||||
struct scst_session *sess;
|
||||
|
||||
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
|
||||
if (sess->shut_phase == SCST_SESS_SPH_READY) {
|
||||
/*
|
||||
@@ -870,6 +872,7 @@ static void __printf(2, 3) scst_to_syslog(void *arg, const char *fmt, ...)
|
||||
int scst_get_cmd_counter(void)
|
||||
{
|
||||
int i, res = 0;
|
||||
|
||||
for (i = 0; i < (int)ARRAY_SIZE(scst_percpu_infos); i++)
|
||||
res += atomic_read(&scst_percpu_infos[i].cpu_cmd_count);
|
||||
return res;
|
||||
@@ -1467,6 +1470,10 @@ int scst_register_virtual_device(struct scst_dev_type *dev_handler,
|
||||
|
||||
list_add_tail(&dev->dev_list_entry, &scst_dev_list);
|
||||
|
||||
res = scst_cm_on_dev_register(dev);
|
||||
if (res != 0)
|
||||
goto out_unreg;
|
||||
|
||||
mutex_unlock(&scst_mutex);
|
||||
scst_resume_activity();
|
||||
|
||||
@@ -1478,6 +1485,12 @@ out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
out_unreg:
|
||||
dev->dev_unregistering = 1;
|
||||
list_del(&dev->dev_list_entry);
|
||||
scst_assign_dev_handler(dev, &scst_null_devtype);
|
||||
goto out_pr_clear_dev;
|
||||
|
||||
#ifndef CONFIG_SCST_PROC
|
||||
out_lock_pr_clear_dev:
|
||||
mutex_lock(&scst_mutex);
|
||||
@@ -1528,6 +1541,10 @@ void scst_unregister_virtual_device(int id)
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
dev->dev_unregistering = 1;
|
||||
|
||||
scst_cm_on_dev_unregister(dev);
|
||||
|
||||
list_del_init(&dev->dev_list_entry);
|
||||
|
||||
scst_pr_clear_dev(dev);
|
||||
@@ -1861,6 +1878,7 @@ int scst_add_threads(struct scst_cmd_threads *cmd_threads,
|
||||
|
||||
if (tgt_dev != NULL) {
|
||||
struct scst_tgt_dev *t;
|
||||
|
||||
list_for_each_entry(t, &tgt_dev->dev->dev_tgt_dev_list,
|
||||
dev_tgt_dev_list_entry) {
|
||||
if (t == tgt_dev)
|
||||
@@ -2431,7 +2449,8 @@ static int __init init_scst(void)
|
||||
struct scsi_sense_hdr *shdr;
|
||||
struct scst_order_data *o;
|
||||
struct scst_cmd *c;
|
||||
BUILD_BUG_ON(SCST_SENSE_BUFFERSIZE < sizeof(*shdr));
|
||||
|
||||
BUILD_BUG_ON(sizeof(*shdr) > SCST_SENSE_BUFFERSIZE);
|
||||
BUILD_BUG_ON(sizeof(o->curr_sn) != sizeof(o->expected_sn));
|
||||
BUILD_BUG_ON(sizeof(c->sn) != sizeof(o->expected_sn));
|
||||
}
|
||||
@@ -2578,14 +2597,19 @@ static int __init init_scst(void)
|
||||
goto out_destroy_sense_mempool;
|
||||
}
|
||||
|
||||
res = scst_sysfs_init();
|
||||
res = scst_event_init();
|
||||
if (res != 0)
|
||||
goto out_destroy_aen_mempool;
|
||||
|
||||
res = scst_sysfs_init();
|
||||
if (res != 0)
|
||||
goto out_event_exit;
|
||||
|
||||
scst_tg_init();
|
||||
|
||||
if (scst_max_cmd_mem == 0) {
|
||||
struct sysinfo si;
|
||||
|
||||
si_meminfo(&si);
|
||||
#if BITS_PER_LONG == 32
|
||||
scst_max_cmd_mem = min(
|
||||
@@ -2651,6 +2675,15 @@ static int __init init_scst(void)
|
||||
goto out_thread_free;
|
||||
#endif
|
||||
|
||||
res = scst_cm_init();
|
||||
if (res != 0)
|
||||
#ifdef CONFIG_SCST_PROC
|
||||
goto out_proc_cleanup;
|
||||
#else
|
||||
goto out_thread_free;
|
||||
#endif
|
||||
|
||||
|
||||
PRINT_INFO("SCST version %s loaded successfully (max mem for "
|
||||
"commands %dMB, per device %dMB)", SCST_VERSION_STRING,
|
||||
scst_max_cmd_mem, scst_max_dev_cmd_mem);
|
||||
@@ -2661,6 +2694,11 @@ out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
#ifdef CONFIG_SCST_PROC
|
||||
out_proc_cleanup:
|
||||
scst_proc_cleanup_module();
|
||||
#endif
|
||||
|
||||
out_thread_free:
|
||||
scst_stop_global_threads();
|
||||
|
||||
@@ -2678,6 +2716,9 @@ out_destroy_sgv_pool:
|
||||
out_sysfs_cleanup:
|
||||
scst_sysfs_cleanup();
|
||||
|
||||
out_event_exit:
|
||||
scst_event_exit();
|
||||
|
||||
out_destroy_aen_mempool:
|
||||
mempool_destroy(scst_aen_mempool);
|
||||
|
||||
@@ -2740,6 +2781,8 @@ static void __exit exit_scst(void)
|
||||
|
||||
/* ToDo: unregister_cpu_notifier() */
|
||||
|
||||
scst_cm_exit();
|
||||
|
||||
#ifdef CONFIG_SCST_PROC
|
||||
scst_proc_cleanup_module();
|
||||
#endif
|
||||
@@ -2759,6 +2802,8 @@ static void __exit exit_scst(void)
|
||||
|
||||
scst_sysfs_cleanup();
|
||||
|
||||
scst_event_exit();
|
||||
|
||||
#define DEINIT_CACHEP(p) do { \
|
||||
kmem_cache_destroy(p); \
|
||||
p = NULL; \
|
||||
|
||||
+6
-1
@@ -675,6 +675,7 @@ static int sgv_alloc_sg_entries(struct scatterlist *sg, int pages,
|
||||
pg = 0;
|
||||
for (i = 0; i < pages; i++) {
|
||||
int n = PAGE_ALIGN(sg[i].length) >> PAGE_SHIFT;
|
||||
|
||||
trans_tbl[i].pg_count = pg;
|
||||
for (j = 0; j < n; j++)
|
||||
trans_tbl[pg++].sg_num = i+1;
|
||||
@@ -972,6 +973,7 @@ struct scatterlist *sgv_pool_alloc(struct sgv_pool *pool, unsigned int size,
|
||||
pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
|
||||
if (pool->single_alloc_pages == 0) {
|
||||
int pages_order = get_order(size);
|
||||
|
||||
cache_num = pages_order;
|
||||
pages_to_alloc = (1 << pages_order);
|
||||
} else {
|
||||
@@ -1128,6 +1130,7 @@ struct scatterlist *sgv_pool_alloc(struct sgv_pool *pool, unsigned int size,
|
||||
success:
|
||||
if (cache_num >= 0) {
|
||||
int sg;
|
||||
|
||||
atomic_inc(&pool->cache_acc[cache_num].total_alloc);
|
||||
if (sgv_pool_clustered(pool))
|
||||
cnt = obj->trans_tbl[pages-1].sg_num;
|
||||
@@ -1255,10 +1258,12 @@ void sgv_pool_free(struct sgv_pool_obj *obj, struct scst_mem_lim *mem_lim)
|
||||
{
|
||||
struct scatterlist *sg = obj->sg_entries;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < obj->sg_count; i++) {
|
||||
struct page *p = sg_page(&sg[i]);
|
||||
int len = sg[i].length;
|
||||
int pages = PAGE_ALIGN(len) >> PAGE_SHIFT;
|
||||
|
||||
while (pages > 0) {
|
||||
if (page_count(p) != 1) {
|
||||
PRINT_WARNING("Freeing page %p with "
|
||||
@@ -1327,7 +1332,7 @@ struct scatterlist *scst_alloc_sg(int size, gfp_t gfp_mask, int *count)
|
||||
}
|
||||
}
|
||||
|
||||
res = kmalloc(pages*sizeof(*res), gfp_mask);
|
||||
res = kmalloc_array(pages, sizeof(*res), gfp_mask);
|
||||
if (res == NULL) {
|
||||
TRACE(TRACE_OUT_OF_MEM, "Unable to allocate sg for %d pages",
|
||||
pages);
|
||||
|
||||
+11
-5
@@ -103,6 +103,7 @@ static inline void tid_secure(uint8_t *tid)
|
||||
{
|
||||
if ((tid[0] & 0x0f) == SCSI_TRANSPORTID_PROTOCOLID_ISCSI) {
|
||||
int size = scst_tid_size(tid);
|
||||
|
||||
tid[size - 1] = '\0';
|
||||
}
|
||||
|
||||
@@ -110,7 +111,7 @@ static inline void tid_secure(uint8_t *tid)
|
||||
}
|
||||
|
||||
/* Returns false if tid's are not equal, true otherwise */
|
||||
static bool tid_equal(const uint8_t *tid_a, const uint8_t *tid_b)
|
||||
bool tid_equal(const uint8_t *tid_a, const uint8_t *tid_b)
|
||||
{
|
||||
int len;
|
||||
|
||||
@@ -137,6 +138,7 @@ static bool tid_equal(const uint8_t *tid_a, const uint8_t *tid_b)
|
||||
else if (tid_a_fmt == 0x40) {
|
||||
if (tid_a_fmt != tid_b_fmt) {
|
||||
uint8_t *p = strnchr(tid_a, tid_a_max, ',');
|
||||
|
||||
if (p == NULL)
|
||||
goto out_error;
|
||||
tid_a_len = p - tid_a;
|
||||
@@ -153,6 +155,7 @@ static bool tid_equal(const uint8_t *tid_a, const uint8_t *tid_b)
|
||||
else if (tid_b_fmt == 0x40) {
|
||||
if (tid_a_fmt != tid_b_fmt) {
|
||||
uint8_t *p = strnchr(tid_b, tid_b_max, ',');
|
||||
|
||||
if (p == NULL)
|
||||
goto out_error;
|
||||
tid_b_len = p - tid_b;
|
||||
@@ -243,6 +246,7 @@ void scst_pr_dump_prs(struct scst_device *dev, bool force)
|
||||
else {
|
||||
struct scst_dev_registrant *reg;
|
||||
int i = 0;
|
||||
|
||||
list_for_each_entry(reg, &dev->dev_registrants_list,
|
||||
dev_registrants_list_entry) {
|
||||
PRINT_INFO(" [%d] registrant %s/%d, key %016llx "
|
||||
@@ -256,6 +260,7 @@ void scst_pr_dump_prs(struct scst_device *dev, bool force)
|
||||
|
||||
if (dev->pr_is_set) {
|
||||
struct scst_dev_registrant *holder = dev->pr_holder;
|
||||
|
||||
if (holder != NULL)
|
||||
PRINT_INFO("Reservation holder is %s/%d (key %016llx, "
|
||||
"scope %x, type %x, reg %p, tgt_dev %p)",
|
||||
@@ -629,6 +634,7 @@ static void scst_pr_abort_reg(struct scst_device *dev,
|
||||
if ((reg->tgt_dev != pr_cmd->tgt_dev) && !dev->tas) {
|
||||
uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN];
|
||||
int sl;
|
||||
|
||||
sl = scst_set_sense(sense_buffer, sizeof(sense_buffer),
|
||||
dev->d_sense,
|
||||
SCST_LOAD_SENSE(scst_sense_cleared_by_another_ini_UA));
|
||||
@@ -688,11 +694,11 @@ static int scst_pr_do_load_device_file(struct scst_device *dev,
|
||||
|
||||
inode = file_inode(file);
|
||||
|
||||
if (S_ISREG(inode->i_mode))
|
||||
/* Nothing to do */;
|
||||
else if (S_ISBLK(inode->i_mode))
|
||||
if (S_ISREG(inode->i_mode)) {
|
||||
/* Nothing to do */
|
||||
} else if (S_ISBLK(inode->i_mode)) {
|
||||
inode = inode->i_bdev->bd_inode;
|
||||
else {
|
||||
} else {
|
||||
PRINT_ERROR("Invalid file mode 0x%x", inode->i_mode);
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
@@ -131,6 +131,8 @@ void scst_pr_read_full_status(struct scst_cmd *cmd, uint8_t *buffer,
|
||||
int buffer_size);
|
||||
|
||||
int scst_tid_size(const uint8_t *tid);
|
||||
bool tid_equal(const uint8_t *tid_a, const uint8_t *tid_b);
|
||||
|
||||
struct scst_dev_registrant *scst_pr_find_reg(struct scst_device *dev,
|
||||
const uint8_t *transport_id, const uint16_t rel_tgt_id);
|
||||
struct scst_dev_registrant *scst_pr_add_registrant(struct scst_device *dev,
|
||||
|
||||
+106
-4
@@ -458,7 +458,6 @@ struct scst_dev_group *scst_lookup_dg_by_kobj(struct kobject *kobj);
|
||||
int scst_dg_dev_add(struct scst_dev_group *dg, const char *name);
|
||||
int scst_dg_dev_remove_by_name(struct scst_dev_group *dg, const char *name);
|
||||
int scst_dg_dev_remove_by_dev(struct scst_device *dev);
|
||||
const char *scst_alua_state_name(enum scst_tg_state s);
|
||||
enum scst_tg_state scst_alua_name_to_state(const char *n);
|
||||
int scst_tg_add(struct scst_dev_group *dg, const char *name);
|
||||
int scst_tg_remove_by_name(struct scst_dev_group *dg, const char *name);
|
||||
@@ -716,10 +715,18 @@ void scst_gen_aen_or_ua(struct scst_tgt_dev *tgt_dev,
|
||||
* we don't care about all commands that already on the device.
|
||||
*/
|
||||
|
||||
extern void scst_block_dev(struct scst_device *dev);
|
||||
extern void scst_unblock_dev(struct scst_device *dev);
|
||||
|
||||
void scst_block_dev(struct scst_device *dev);
|
||||
void scst_unblock_dev(struct scst_device *dev);
|
||||
bool scst_do_check_blocked_dev(struct scst_cmd *cmd);
|
||||
bool __scst_check_blocked_dev(struct scst_cmd *cmd);
|
||||
void __scst_check_unblock_dev(struct scst_cmd *cmd);
|
||||
void scst_check_unblock_dev(struct scst_cmd *cmd);
|
||||
|
||||
int scst_ext_block_dev(struct scst_device *dev, bool sync,
|
||||
ext_blocker_done_fn_t done_fn, const uint8_t *priv, int priv_len);
|
||||
void scst_ext_unblock_dev(struct scst_device *dev, bool stpg);
|
||||
void __scst_ext_blocking_done(struct scst_device *dev);
|
||||
void scst_ext_blocking_done(struct scst_device *dev);
|
||||
|
||||
/*
|
||||
* Increases global SCST ref counters which prevent from entering into suspended
|
||||
@@ -758,6 +765,7 @@ static inline atomic_t *scst_get(void)
|
||||
static inline void scst_put(atomic_t *a)
|
||||
{
|
||||
int f;
|
||||
|
||||
f = atomic_dec_and_test(a);
|
||||
/* See comment about smp_mb() in scst_suspend_activity() */
|
||||
if (unlikely(test_bit(SCST_FLAG_SUSPENDED, &scst_flags)) && f) {
|
||||
@@ -815,6 +823,8 @@ void scst_unthrottle_cmd(struct scst_cmd *cmd);
|
||||
int scst_do_internal_parsing(struct scst_cmd *cmd);
|
||||
int scst_parse_descriptors(struct scst_cmd *cmd);
|
||||
|
||||
int scst_cmp_wr_local(struct scst_cmd *cmd);
|
||||
|
||||
int scst_pr_init(struct scst_device *dev);
|
||||
void scst_pr_cleanup(struct scst_device *dev);
|
||||
|
||||
@@ -826,6 +836,87 @@ void scst_vfs_unlink_and_put(struct path *path);
|
||||
|
||||
int scst_copy_file(const char *src, const char *dest);
|
||||
|
||||
struct scst_cmd *__scst_create_prepare_internal_cmd(const uint8_t *cdb,
|
||||
unsigned int cdb_len, enum scst_cmd_queue_type queue_type,
|
||||
struct scst_tgt_dev *tgt_dev, gfp_t gfp_mask, bool fantom);
|
||||
|
||||
static inline bool scst_lba1_inside_lba2(int64_t lba1,
|
||||
int64_t lba2, int64_t lba2_blocks)
|
||||
{
|
||||
bool res;
|
||||
|
||||
TRACE_DBG("lba1 %lld, lba2 %lld, lba2_blocks %lld", (long long)lba1,
|
||||
(long long)lba2, (long long)lba2_blocks);
|
||||
|
||||
if ((lba1 >= lba2) && (lba1 < (lba2 + lba2_blocks)))
|
||||
res = true;
|
||||
else
|
||||
res = false;
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SCST_PROC
|
||||
|
||||
int scst_cm_on_dev_register(struct scst_device *dev);
|
||||
void scst_cm_on_dev_unregister(struct scst_device *dev);
|
||||
|
||||
int scst_cm_parse_descriptors(struct scst_cmd *cmd);
|
||||
void scst_cm_free_descriptors(struct scst_cmd *cmd);
|
||||
|
||||
int scst_cm_ext_copy_exec(struct scst_cmd *cmd);
|
||||
int scst_cm_rcv_copy_res_exec(struct scst_cmd *cmd);
|
||||
|
||||
void sess_cm_list_id_cleanup_work_fn(struct delayed_work *work);
|
||||
void scst_cm_free_pending_list_ids(struct scst_session *sess);
|
||||
|
||||
bool scst_cm_check_block_all_devs(struct scst_cmd *cmd);
|
||||
void scst_cm_abort_ec_cmd(struct scst_cmd *ec_cmd);
|
||||
|
||||
bool scst_cm_ec_cmd_overlap(struct scst_cmd *ec_cmd, struct scst_cmd *cmd);
|
||||
|
||||
int scst_cm_init(void);
|
||||
void scst_cm_exit(void);
|
||||
|
||||
#else /* #ifndef CONFIG_SCST_PROC */
|
||||
|
||||
static inline int scst_cm_on_dev_register(struct scst_device *dev) { return 0; }
|
||||
static inline void scst_cm_on_dev_unregister(struct scst_device *dev) {}
|
||||
|
||||
static inline int scst_cm_parse_descriptors(struct scst_cmd *cmd)
|
||||
{
|
||||
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_opcode));
|
||||
scst_set_cmd_abnormal_done_state(cmd);
|
||||
return -1;
|
||||
}
|
||||
static inline void scst_cm_free_descriptors(struct scst_cmd *cmd) {}
|
||||
|
||||
static inline int scst_cm_ext_copy_exec(struct scst_cmd *cmd)
|
||||
{
|
||||
return SCST_EXEC_NOT_COMPLETED;
|
||||
}
|
||||
static inline int scst_cm_rcv_copy_res_exec(struct scst_cmd *cmd)
|
||||
{
|
||||
return SCST_EXEC_NOT_COMPLETED;
|
||||
}
|
||||
|
||||
static inline void sess_cm_list_id_cleanup_work_fn(struct work_struct *work) {}
|
||||
static inline void scst_cm_free_pending_list_ids(struct scst_session *sess) {}
|
||||
|
||||
static inline bool scst_cm_check_block_all_devs(struct scst_cmd *cmd) { return false; }
|
||||
static inline void scst_cm_abort_ec_cmd(struct scst_cmd *ec_cmd) {}
|
||||
|
||||
static inline bool scst_cm_ec_cmd_overlap(struct scst_cmd *ec_cmd, struct scst_cmd *cmd)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int scst_cm_init(void) { return 0; }
|
||||
static inline void scst_cm_exit(void) {}
|
||||
|
||||
#endif /* #ifndef CONFIG_SCST_PROC */
|
||||
|
||||
#ifdef CONFIG_SCST_DEBUG_TM
|
||||
extern void tm_dbg_check_released_cmds(void);
|
||||
extern int tm_dbg_check_cmd(struct scst_cmd *cmd);
|
||||
@@ -863,6 +954,15 @@ int gen_relative_target_port_id(uint16_t *id);
|
||||
bool scst_is_relative_target_port_id_unique(uint16_t id,
|
||||
const struct scst_tgt *t);
|
||||
|
||||
int scst_event_init(void);
|
||||
void scst_event_exit(void);
|
||||
|
||||
int scst_event_queue_lun_not_found(const struct scst_cmd *cmd);
|
||||
int scst_event_queue_negative_luns_inquiry(const struct scst_tgt *tgt,
|
||||
const char *initiator_name);
|
||||
int scst_event_queue_ext_blocking_done(struct scst_device *dev, void *data, int len);
|
||||
int scst_event_queue_tm_fn_received(struct scst_mgmt_cmd *mcmd);
|
||||
|
||||
typedef void __printf(2, 3) (*scst_show_fn)(void *arg, const char *fmt, ...);
|
||||
void scst_trace_cmds(scst_show_fn show, void *arg);
|
||||
void scst_trace_mcmds(scst_show_fn show, void *arg);
|
||||
@@ -876,6 +976,7 @@ void scst_set_alloc_buf_time(struct scst_cmd *cmd);
|
||||
void scst_set_restart_waiting_time(struct scst_cmd *cmd);
|
||||
void scst_set_rdy_to_xfer_time(struct scst_cmd *cmd);
|
||||
void scst_set_pre_exec_time(struct scst_cmd *cmd);
|
||||
void scst_set_exec_start(struct scst_cmd *cmd);
|
||||
void scst_set_exec_time(struct scst_cmd *cmd);
|
||||
void scst_set_dev_done_time(struct scst_cmd *cmd);
|
||||
void scst_set_xmit_time(struct scst_cmd *cmd);
|
||||
@@ -890,6 +991,7 @@ static inline void scst_set_alloc_buf_time(struct scst_cmd *cmd) {}
|
||||
static inline void scst_set_restart_waiting_time(struct scst_cmd *cmd) {}
|
||||
static inline void scst_set_rdy_to_xfer_time(struct scst_cmd *cmd) {}
|
||||
static inline void scst_set_pre_exec_time(struct scst_cmd *cmd) {}
|
||||
static inline void scst_set_exec_start(struct scst_cmd *cmd) {}
|
||||
static inline void scst_set_exec_time(struct scst_cmd *cmd) {}
|
||||
static inline void scst_set_dev_done_time(struct scst_cmd *cmd) {}
|
||||
static inline void scst_set_xmit_time(struct scst_cmd *cmd) {}
|
||||
|
||||
+149
-58
@@ -272,11 +272,7 @@ static int scst_write_trace(const char *buf, size_t length,
|
||||
}
|
||||
break;
|
||||
case SCST_TRACE_ACTION_VALUE:
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
|
||||
res = kstrtoul(p, 0, &level);
|
||||
#else
|
||||
res = strict_strtoul(p, 0, &level);
|
||||
#endif
|
||||
if (res != 0) {
|
||||
PRINT_ERROR("Invalid trace value \"%s\"", p);
|
||||
res = -EINVAL;
|
||||
@@ -668,9 +664,11 @@ static int scst_check_tgt_acg_ptrs(struct scst_tgt *tgt, struct scst_acg *acg)
|
||||
|
||||
list_for_each_entry(tgtt, &scst_template_list, scst_template_list_entry) {
|
||||
struct scst_tgt *t;
|
||||
|
||||
list_for_each_entry(t, &tgtt->tgt_list, tgt_list_entry) {
|
||||
if (t == tgt) {
|
||||
struct scst_acg *a;
|
||||
|
||||
if (acg == NULL)
|
||||
goto out;
|
||||
if (acg == tgt->default_acg)
|
||||
@@ -783,8 +781,8 @@ static ssize_t scst_show(struct kobject *kobj, struct attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct kobj_attribute *kobj_attr;
|
||||
kobj_attr = container_of(attr, struct kobj_attribute, attr);
|
||||
|
||||
kobj_attr = container_of(attr, struct kobj_attribute, attr);
|
||||
return kobj_attr->show(kobj, kobj_attr, buf);
|
||||
}
|
||||
|
||||
@@ -792,8 +790,8 @@ static ssize_t scst_store(struct kobject *kobj, struct attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct kobj_attribute *kobj_attr;
|
||||
kobj_attr = container_of(attr, struct kobj_attribute, attr);
|
||||
|
||||
kobj_attr = container_of(attr, struct kobj_attribute, attr);
|
||||
if (kobj_attr->store)
|
||||
return kobj_attr->store(kobj, kobj_attr, buf, count);
|
||||
else
|
||||
@@ -1073,6 +1071,7 @@ static ssize_t scst_tgtt_dif_capable_show(struct kobject *kobj,
|
||||
if (tgtt->supported_dif_block_sizes) {
|
||||
const int *p = tgtt->supported_dif_block_sizes;
|
||||
int j;
|
||||
|
||||
pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos,
|
||||
"Supported blocks: ");
|
||||
j = pos;
|
||||
@@ -1445,11 +1444,7 @@ static int __scst_process_luns_mgmt_store(char *buffer,
|
||||
break;
|
||||
case SCST_LUN_ACTION_DEL:
|
||||
p = scst_get_next_lexem(&pp);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
|
||||
res = kstrtoul(p, 0, &virt_lun);
|
||||
#else
|
||||
res = strict_strtoul(p, 0, &virt_lun);
|
||||
#endif
|
||||
if (res != 0)
|
||||
goto out_unlock;
|
||||
|
||||
@@ -1758,11 +1753,7 @@ static ssize_t __scst_acg_io_grouping_type_store(struct scst_acg *acg,
|
||||
min_t(int, strlen(SCST_IO_GROUPING_NEVER_STR), count)) == 0)
|
||||
io_grouping_type = SCST_IO_GROUPING_NEVER;
|
||||
else {
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
|
||||
res = kstrtol(buf, 0, &io_grouping_type);
|
||||
#else
|
||||
res = strict_strtol(buf, 0, &io_grouping_type);
|
||||
#endif
|
||||
if ((res != 0) || (io_grouping_type <= 0)) {
|
||||
PRINT_ERROR("Unknown or not allowed I/O grouping type "
|
||||
"%s", buf);
|
||||
@@ -1892,6 +1883,7 @@ static ssize_t __scst_acg_black_hole_store(struct scst_acg *acg,
|
||||
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
|
||||
struct list_head *head = &sess->sess_tgt_dev_list[i];
|
||||
struct scst_tgt_dev *tgt_dev;
|
||||
|
||||
list_for_each_entry_rcu(tgt_dev, head,
|
||||
sess_tgt_dev_list_entry) {
|
||||
if (t != SCST_ACG_BLACK_HOLE_NONE)
|
||||
@@ -2472,11 +2464,7 @@ static ssize_t scst_rel_tgt_id_store(struct kobject *kobj,
|
||||
|
||||
tgt = container_of(kobj, struct scst_tgt, tgt_kobj);
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
|
||||
res = kstrtoul(buf, 0, &rel_tgt_id);
|
||||
#else
|
||||
res = strict_strtoul(buf, 0, &rel_tgt_id);
|
||||
#endif
|
||||
if (res != 0) {
|
||||
PRINT_ERROR("%s", "Wrong rel_tgt_id");
|
||||
res = -EINVAL;
|
||||
@@ -2564,9 +2552,11 @@ static ssize_t scst_tgt_forwarding_store(struct kobject *kobj,
|
||||
|
||||
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
|
||||
struct list_head *head = &sess->sess_tgt_dev_list[i];
|
||||
struct scst_tgt_dev *tgt_dev;
|
||||
|
||||
list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) {
|
||||
if (tgt->tgt_forwarding)
|
||||
set_bit(SCST_TGT_DEV_FORWARDING, &tgt_dev->tgt_dev_flags);
|
||||
@@ -2729,6 +2719,7 @@ static ssize_t scst_tgt_dif_capable_show(struct kobject *kobj,
|
||||
if (tgt->tgt_supported_dif_block_sizes) {
|
||||
const int *p = tgt->tgt_supported_dif_block_sizes;
|
||||
int j;
|
||||
|
||||
pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos,
|
||||
"Supported blocks: ");
|
||||
j = pos;
|
||||
@@ -3339,13 +3330,9 @@ static ssize_t scst_dev_sysfs_threads_num_store(struct kobject *kobj,
|
||||
|
||||
dev = container_of(kobj, struct scst_device, dev_kobj);
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
|
||||
res = kstrtol(buf, 0, &newtn);
|
||||
#else
|
||||
res = strict_strtol(buf, 0, &newtn);
|
||||
#endif
|
||||
if (res != 0) {
|
||||
PRINT_ERROR("strict_strtol() for %s failed: %d ", buf, res);
|
||||
PRINT_ERROR("kstrtol() for %s failed: %d ", buf, res);
|
||||
goto out;
|
||||
}
|
||||
if (newtn < 0) {
|
||||
@@ -3473,8 +3460,137 @@ static struct kobj_attribute dev_threads_pool_type_attr =
|
||||
scst_dev_sysfs_threads_pool_type_show,
|
||||
scst_dev_sysfs_threads_pool_type_store);
|
||||
|
||||
static ssize_t scst_dev_block_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
int pos = 0;
|
||||
struct scst_device *dev;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
dev = container_of(kobj, struct scst_device, dev_kobj);
|
||||
|
||||
pos = sprintf(buf, "%d %d\n", ACCESS_ONCE(dev->ext_blocks_cnt),
|
||||
dev->ext_blocking_pending);
|
||||
|
||||
TRACE_EXIT_RES(pos);
|
||||
return pos;
|
||||
}
|
||||
|
||||
static void scst_sysfs_ext_blocking_done(struct scst_device *dev,
|
||||
uint8_t *data, int len)
|
||||
{
|
||||
scst_event_queue_ext_blocking_done(dev, data, len);
|
||||
}
|
||||
|
||||
static ssize_t scst_dev_block_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
int res, data_len = 0, pos = 0;
|
||||
struct scst_device *dev;
|
||||
const char *p = buf, *data_start = NULL;
|
||||
bool sync;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
dev = container_of(kobj, struct scst_device, dev_kobj);
|
||||
|
||||
switch (*p) {
|
||||
case '0':
|
||||
p++;
|
||||
pos++;
|
||||
while ((pos < count) && isspace(*p) && (*p != '\0')) {
|
||||
p++;
|
||||
pos++;
|
||||
}
|
||||
if ((pos != count) && (*p != '\0')) {
|
||||
PRINT_ERROR("Parse error on %c", *p);
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
TRACE_MGMT_DBG("Sysfs unblocking (dev %s)", dev->virt_name);
|
||||
|
||||
scst_ext_unblock_dev(dev, false);
|
||||
res = 0;
|
||||
goto out;
|
||||
case '1':
|
||||
p++;
|
||||
pos++;
|
||||
while ((pos < count) && isspace(*p) && (*p != '\0')) {
|
||||
p++;
|
||||
pos++;
|
||||
}
|
||||
if ((pos == count) || (*p == '\0')) {
|
||||
data_len = sizeof(void *);
|
||||
sync = true;
|
||||
break;
|
||||
} else if (*p != '1') {
|
||||
PRINT_ERROR("Parse error on %c", *p);
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sync = false;
|
||||
|
||||
p++;
|
||||
pos++;
|
||||
if ((pos == count) || (*p == '\0'))
|
||||
break;
|
||||
|
||||
while ((pos < count) && isspace(*p) && (*p != '\0')) {
|
||||
p++;
|
||||
pos++;
|
||||
}
|
||||
if ((pos == count) || (*p == '\0'))
|
||||
break;
|
||||
|
||||
data_start = p;
|
||||
while ((pos < count) && (*p != '\0')) {
|
||||
p++;
|
||||
pos++;
|
||||
data_len++;
|
||||
}
|
||||
/* Skip trailing spaces, if any */
|
||||
while (isspace(*(p-1))) {
|
||||
p--;
|
||||
data_len--;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
PRINT_ERROR("Illegal blocking value %c", *p);
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
TRACE_MGMT_DBG("Sysfs blocking dev %s (sync %d, data_start %p, "
|
||||
"data_len %d)", dev->virt_name, sync, data_start, data_len);
|
||||
|
||||
if (sync)
|
||||
res = scst_ext_block_dev(dev, true, NULL, NULL, 0);
|
||||
else
|
||||
res = scst_ext_block_dev(dev, false, scst_sysfs_ext_blocking_done,
|
||||
data_start, data_len);
|
||||
if (res != 0)
|
||||
goto out;
|
||||
|
||||
res = 0;
|
||||
|
||||
out:
|
||||
if (res == 0)
|
||||
res = count;
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static struct kobj_attribute dev_block_attr =
|
||||
__ATTR(block, S_IRUGO | S_IWUSR, scst_dev_block_show,
|
||||
scst_dev_block_store);
|
||||
|
||||
static struct attribute *scst_dev_attrs[] = {
|
||||
&dev_type_attr.attr,
|
||||
&dev_block_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -3771,11 +3887,7 @@ static ssize_t scst_dev_sysfs_dif_static_app_tag_store(struct kobject *kobj,
|
||||
|
||||
dev = container_of(kobj, struct scst_device, dev_kobj);
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
|
||||
res = kstrtoull(buf, 0, &val);
|
||||
#else
|
||||
res = strict_strtoull(buf, 0, &val);
|
||||
#endif
|
||||
if (res != 0) {
|
||||
PRINT_ERROR("strtoul() for %s failed: %d (device %s)",
|
||||
buf, res, dev->virt_name);
|
||||
@@ -4333,6 +4445,7 @@ static int scst_sess_zero_latency(struct scst_sysfs_work_item *work)
|
||||
for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) {
|
||||
struct list_head *head = &sess->sess_tgt_dev_list[t];
|
||||
struct scst_tgt_dev *tgt_dev;
|
||||
|
||||
list_for_each_entry_rcu(tgt_dev, head,
|
||||
sess_tgt_dev_list_entry) {
|
||||
tgt_dev->scst_time = 0;
|
||||
@@ -4412,6 +4525,7 @@ static int scst_sysfs_sess_get_active_commands(struct scst_session *sess)
|
||||
for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) {
|
||||
struct list_head *head = &sess->sess_tgt_dev_list[t];
|
||||
struct scst_tgt_dev *tgt_dev;
|
||||
|
||||
list_for_each_entry_rcu(tgt_dev, head,
|
||||
sess_tgt_dev_list_entry) {
|
||||
active_cmds += atomic_read(&tgt_dev->tgt_dev_cmd_count);
|
||||
@@ -4477,6 +4591,7 @@ static int scst_sysfs_sess_get_dif_checks_failed_work_fn(struct scst_sysfs_work_
|
||||
for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) {
|
||||
struct list_head *head = &sess->sess_tgt_dev_list[t];
|
||||
struct scst_tgt_dev *tgt_dev;
|
||||
|
||||
list_for_each_entry_rcu(tgt_dev, head, sess_tgt_dev_list_entry) {
|
||||
app_failed_tgt += atomic_read(&tgt_dev->tgt_dev_dif_app_failed_tgt);
|
||||
ref_failed_tgt += atomic_read(&tgt_dev->tgt_dev_dif_ref_failed_tgt);
|
||||
@@ -4552,6 +4667,7 @@ static int scst_sess_zero_dif_checks_failed(struct scst_sysfs_work_item *work)
|
||||
for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) {
|
||||
struct list_head *head = &sess->sess_tgt_dev_list[t];
|
||||
struct scst_tgt_dev *tgt_dev;
|
||||
|
||||
list_for_each_entry_rcu(tgt_dev, head,
|
||||
sess_tgt_dev_list_entry) {
|
||||
atomic_set(&tgt_dev->tgt_dev_dif_app_failed_tgt, 0);
|
||||
@@ -6153,11 +6269,7 @@ static ssize_t scst_tg_tgt_rel_tgt_id_store(struct kobject *kobj,
|
||||
TRACE_ENTRY();
|
||||
tg_tgt = container_of(kobj, struct scst_tg_tgt, kobj);
|
||||
snprintf(ch, sizeof(ch), "%.*s", min_t(int, count, sizeof(ch)-1), buf);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
|
||||
res = kstrtoul(ch, 0, &rel_tgt_id);
|
||||
#else
|
||||
res = strict_strtoul(ch, 0, &rel_tgt_id);
|
||||
#endif
|
||||
if (res)
|
||||
goto out;
|
||||
res = -EINVAL;
|
||||
@@ -6247,11 +6359,7 @@ static ssize_t scst_tg_group_id_store(struct kobject *kobj,
|
||||
TRACE_ENTRY();
|
||||
tg = container_of(kobj, struct scst_target_group, kobj);
|
||||
snprintf(ch, sizeof(ch), "%.*s", min_t(int, count, sizeof(ch)-1), buf);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
|
||||
res = kstrtoul(ch, 0, &group_id);
|
||||
#else
|
||||
res = strict_strtoul(ch, 0, &group_id);
|
||||
#endif
|
||||
if (res)
|
||||
goto out;
|
||||
res = -EINVAL;
|
||||
@@ -6289,11 +6397,7 @@ static int scst_tg_preferred_store_work_fn(struct scst_sysfs_work_item *w)
|
||||
TRACE_ENTRY();
|
||||
cmd = w->buf;
|
||||
tg = container_of(w->kobj, struct scst_target_group, kobj);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
|
||||
res = kstrtoul(cmd, 0, &preferred);
|
||||
#else
|
||||
res = strict_strtoul(cmd, 0, &preferred);
|
||||
#endif
|
||||
if (res)
|
||||
goto out;
|
||||
res = -EINVAL;
|
||||
@@ -6827,13 +6931,9 @@ static ssize_t scst_threads_store(struct kobject *kobj,
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
|
||||
res = kstrtol(buf, 0, &newtn);
|
||||
#else
|
||||
res = strict_strtol(buf, 0, &newtn);
|
||||
#endif
|
||||
if (res != 0) {
|
||||
PRINT_ERROR("strict_strtol() for %s failed: %d ", buf, res);
|
||||
PRINT_ERROR("kstrtol() for %s failed: %d ", buf, res);
|
||||
goto out;
|
||||
}
|
||||
if (newtn <= 0) {
|
||||
@@ -6883,13 +6983,9 @@ static ssize_t scst_setup_id_store(struct kobject *kobj,
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
|
||||
res = kstrtoul(buf, 0, &val);
|
||||
#else
|
||||
res = strict_strtoul(buf, 0, &val);
|
||||
#endif
|
||||
if (res != 0) {
|
||||
PRINT_ERROR("strict_strtoul() for %s failed: %d ", buf, res);
|
||||
PRINT_ERROR("kstrtoul() for %s failed: %d ", buf, res);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -6930,13 +7026,9 @@ static ssize_t scst_max_tasklet_cmd_store(struct kobject *kobj,
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
|
||||
res = kstrtoul(buf, 0, &val);
|
||||
#else
|
||||
res = strict_strtoul(buf, 0, &val);
|
||||
#endif
|
||||
if (res != 0) {
|
||||
PRINT_ERROR("strict_strtoul() for %s failed: %d ", buf, res);
|
||||
PRINT_ERROR("kstrtoul() for %s failed: %d ", buf, res);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -7393,10 +7485,9 @@ int scst_wait_info_completion(struct scst_sysfs_user_info *info,
|
||||
break;
|
||||
}
|
||||
} else if (rc != -ERESTARTSYS) {
|
||||
res = rc;
|
||||
PRINT_ERROR("wait_for_completion() failed: %d",
|
||||
res);
|
||||
goto out;
|
||||
res = rc;
|
||||
PRINT_ERROR("wait_for_completion() failed: %d", res);
|
||||
goto out;
|
||||
} else {
|
||||
TRACE_DBG("Waiting for info %p finished with %d, "
|
||||
"retrying", info, rc);
|
||||
|
||||
+532
-37
@@ -103,25 +103,302 @@ static inline void scst_schedule_tasklet(struct scst_cmd *cmd)
|
||||
return;
|
||||
}
|
||||
|
||||
/* No locks */
|
||||
static bool scst_check_blocked_dev(struct scst_cmd *cmd)
|
||||
static bool scst_unmap_overlap(struct scst_cmd *cmd, int64_t lba2,
|
||||
int64_t lba2_blocks)
|
||||
{
|
||||
bool res = false;
|
||||
struct scst_data_descriptor *pd = cmd->cmd_data_descriptors;
|
||||
int i;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (pd == NULL)
|
||||
goto out;
|
||||
|
||||
for (i = 0; pd[i].sdd_blocks != 0; i++) {
|
||||
struct scst_data_descriptor *d = &pd[i];
|
||||
|
||||
TRACE_DBG("i %d, lba %lld, blocks %lld", i,
|
||||
(long long)d->sdd_lba, (long long)d->sdd_blocks);
|
||||
res = scst_lba1_inside_lba2(d->sdd_lba, lba2, lba2_blocks);
|
||||
if (res)
|
||||
goto out;
|
||||
res = scst_lba1_inside_lba2(lba2, d->sdd_lba, d->sdd_blocks);
|
||||
if (res)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool scst_cmd_overlap_cwr(struct scst_cmd *cwr_cmd, struct scst_cmd *cmd)
|
||||
{
|
||||
bool res;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_DBG("cwr_cmd %p, cmd %p (op %s, LBA valid %d, lba %lld, "
|
||||
"len %lld)", cwr_cmd, cmd, scst_get_opcode_name(cmd),
|
||||
(cmd->op_flags & SCST_LBA_NOT_VALID) == 0,
|
||||
(long long)cmd->lba, (long long)cmd->data_len);
|
||||
|
||||
EXTRACHECKS_BUG_ON(cwr_cmd->cdb[0] != COMPARE_AND_WRITE);
|
||||
|
||||
/*
|
||||
* In addition to requirements listed in "Model for uninterrupted
|
||||
* sequences on LBA ranges" (SBC) VMware wants that COMPARE AND WRITE
|
||||
* be atomic against RESERVEs, as well as RESERVEs be atomic against
|
||||
* all COMPARE AND WRITE commands and only against them.
|
||||
*/
|
||||
|
||||
if (cmd->op_flags & SCST_LBA_NOT_VALID) {
|
||||
switch (cmd->cdb[0]) {
|
||||
case RESERVE:
|
||||
case RESERVE_10:
|
||||
res = true;
|
||||
break;
|
||||
case UNMAP:
|
||||
res = scst_unmap_overlap(cmd, cwr_cmd->lba,
|
||||
cwr_cmd->data_len);
|
||||
break;
|
||||
case EXTENDED_COPY:
|
||||
res = scst_cm_ec_cmd_overlap(cmd, cwr_cmd);
|
||||
break;
|
||||
default:
|
||||
res = false;
|
||||
break;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If LBA valid, block_shift must be valid */
|
||||
EXTRACHECKS_BUG_ON(cmd->dev->block_shift <= 0);
|
||||
|
||||
res = scst_lba1_inside_lba2(cwr_cmd->lba, cmd->lba,
|
||||
cmd->data_len >> cmd->dev->block_shift);
|
||||
if (res)
|
||||
goto out;
|
||||
|
||||
res = scst_lba1_inside_lba2(cmd->lba, cwr_cmd->lba,
|
||||
cwr_cmd->data_len >> cwr_cmd->dev->block_shift);
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool scst_cmd_overlap_reserve(struct scst_cmd *reserve_cmd,
|
||||
struct scst_cmd *cmd)
|
||||
{
|
||||
bool res;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_DBG("reserve_cmd %p, cmd %p (op %s, LBA valid %d, lba %lld, "
|
||||
"len %lld)", reserve_cmd, cmd, scst_get_opcode_name(cmd),
|
||||
(cmd->op_flags & SCST_LBA_NOT_VALID) == 0,
|
||||
(long long)cmd->lba, (long long)cmd->data_len);
|
||||
|
||||
EXTRACHECKS_BUG_ON((reserve_cmd->cdb[0] != RESERVE) &&
|
||||
(reserve_cmd->cdb[0] != RESERVE_10));
|
||||
|
||||
/*
|
||||
* In addition to requirements listed in "Model for uninterrupted
|
||||
* sequences on LBA ranges" (SBC) VMware wants that COMPARE AND WRITE
|
||||
* be atomic against RESERVEs, as well as RESERVEs be atomic against
|
||||
* all COMPARE AND WRITE commands and only against them.
|
||||
*/
|
||||
|
||||
if (cmd->cdb[0] == COMPARE_AND_WRITE)
|
||||
res = true;
|
||||
else
|
||||
res = false;
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool scst_cmd_overlap_atomic(struct scst_cmd *atomic_cmd, struct scst_cmd *cmd)
|
||||
{
|
||||
bool res;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_DBG("atomic_cmd %p (op %s), cmd %p (op %s)", atomic_cmd,
|
||||
scst_get_opcode_name(atomic_cmd), cmd, scst_get_opcode_name(cmd));
|
||||
|
||||
EXTRACHECKS_BUG_ON((atomic_cmd->op_flags & SCST_SCSI_ATOMIC) == 0);
|
||||
|
||||
switch (atomic_cmd->cdb[0]) {
|
||||
case COMPARE_AND_WRITE:
|
||||
res = scst_cmd_overlap_cwr(atomic_cmd, cmd);
|
||||
break;
|
||||
case RESERVE:
|
||||
case RESERVE_10:
|
||||
res = scst_cmd_overlap_reserve(atomic_cmd, cmd);
|
||||
break;
|
||||
default:
|
||||
res = false;
|
||||
break;
|
||||
}
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool scst_cmd_overlap(struct scst_cmd *chk_cmd, struct scst_cmd *cmd)
|
||||
{
|
||||
bool res = false;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_DBG("chk_cmd %p, cmd %p", chk_cmd, cmd);
|
||||
|
||||
if ((chk_cmd->op_flags & SCST_SCSI_ATOMIC) != 0)
|
||||
res = scst_cmd_overlap_atomic(chk_cmd, cmd);
|
||||
else if ((cmd->op_flags & SCST_SCSI_ATOMIC) != 0)
|
||||
res = scst_cmd_overlap_atomic(cmd, chk_cmd);
|
||||
else
|
||||
res = false;
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* dev_lock supposed to be held and BH disabled. Returns true if cmd blocked,
|
||||
* hence stop processing it and go to the next command.
|
||||
*/
|
||||
static bool scst_check_scsi_atomicity(struct scst_cmd *chk_cmd)
|
||||
{
|
||||
bool res = false;
|
||||
struct scst_device *dev = chk_cmd->dev;
|
||||
struct scst_cmd *cmd;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
/*
|
||||
* chk_cmd isn't necessary SCSI atomic! For instance, if another SCSI
|
||||
* atomic cmd is waiting blocked.
|
||||
*/
|
||||
|
||||
TRACE_DBG("chk_cmd %p (op %s, internal %d, lba %lld, len %lld)",
|
||||
chk_cmd, scst_get_opcode_name(chk_cmd), chk_cmd->internal,
|
||||
(long long)chk_cmd->lba, (long long)chk_cmd->data_len);
|
||||
|
||||
list_for_each_entry(cmd, &dev->dev_exec_cmd_list, dev_exec_cmd_list_entry) {
|
||||
if (chk_cmd == cmd)
|
||||
continue;
|
||||
if (scst_cmd_overlap(chk_cmd, cmd)) {
|
||||
struct scst_cmd **p = cmd->scsi_atomic_blocked_cmds;
|
||||
|
||||
/*
|
||||
* kmalloc() allocates by at least 32 bytes increments,
|
||||
* hence krealloc() on 8 bytes increments, if not all
|
||||
* that space is used, does nothing.
|
||||
*/
|
||||
p = krealloc(p, sizeof(*p) * (cmd->scsi_atomic_blocked_cmds_count + 1),
|
||||
GFP_ATOMIC);
|
||||
if (p == NULL)
|
||||
goto out_busy_undo;
|
||||
p[cmd->scsi_atomic_blocked_cmds_count] = chk_cmd;
|
||||
cmd->scsi_atomic_blocked_cmds = p;
|
||||
cmd->scsi_atomic_blocked_cmds_count++;
|
||||
|
||||
chk_cmd->scsi_atomic_blockers++;
|
||||
|
||||
TRACE_BLOCK("Delaying cmd %p (op %s, lba %lld, "
|
||||
"len %lld, blockers %d) due to overlap with "
|
||||
"cmd %p (op %s, lba %lld, len %lld, blocked "
|
||||
"cmds %d)", chk_cmd, scst_get_opcode_name(chk_cmd),
|
||||
(long long)chk_cmd->lba,
|
||||
(long long)chk_cmd->data_len,
|
||||
chk_cmd->scsi_atomic_blockers, cmd,
|
||||
scst_get_opcode_name(cmd), (long long)cmd->lba,
|
||||
(long long)cmd->data_len,
|
||||
cmd->scsi_atomic_blocked_cmds_count);
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
out_busy_undo:
|
||||
list_for_each_entry(cmd, &dev->dev_exec_cmd_list, dev_exec_cmd_list_entry) {
|
||||
struct scst_cmd **p = cmd->scsi_atomic_blocked_cmds;
|
||||
|
||||
if ((p != NULL) && (p[cmd->scsi_atomic_blocked_cmds_count-1] == chk_cmd)) {
|
||||
cmd->scsi_atomic_blocked_cmds_count--;
|
||||
chk_cmd->scsi_atomic_blockers--;
|
||||
}
|
||||
}
|
||||
sBUG_ON(chk_cmd->scsi_atomic_blockers != 0);
|
||||
|
||||
scst_set_busy(chk_cmd);
|
||||
scst_set_cmd_abnormal_done_state(chk_cmd);
|
||||
|
||||
spin_lock_irq(&chk_cmd->cmd_threads->cmd_list_lock);
|
||||
TRACE_MGMT_DBG("Adding on error chk_cmd %p back to head of active cmd "
|
||||
"list", chk_cmd);
|
||||
list_add(&chk_cmd->cmd_list_entry, &chk_cmd->cmd_threads->active_cmd_list);
|
||||
wake_up(&chk_cmd->cmd_threads->cmd_list_waitQ);
|
||||
spin_unlock_irq(&chk_cmd->cmd_threads->cmd_list_lock);
|
||||
|
||||
res = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* dev_lock supposed to be BH locked. Returns true if cmd blocked, hence stop
|
||||
* processing it and go to the next command.
|
||||
*/
|
||||
bool scst_do_check_blocked_dev(struct scst_cmd *cmd)
|
||||
{
|
||||
bool res;
|
||||
struct scst_device *dev = cmd->dev;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (unlikely(cmd->internal)) {
|
||||
/*
|
||||
* The original command can already block the device and must
|
||||
* hold reference to it, so internal command should always pass.
|
||||
*/
|
||||
sBUG_ON(dev->on_dev_cmd_count == 0);
|
||||
res = false;
|
||||
goto out;
|
||||
/*
|
||||
* We want to have fairness between just unblocked previously blocked
|
||||
* SCSI atomic cmds and new cmds came after them. Otherwise, the new
|
||||
* cmds can bypass the SCSI atomic cmds and make them unfairly wait
|
||||
* again. So, we need to always, from the beginning, have blocked SCSI
|
||||
* atomic cmds on the exec list, even if they blocked, as well
|
||||
* as dev's SCSI atomic cmds counter incremented.
|
||||
*/
|
||||
|
||||
if (likely(!cmd->on_dev_exec_list)) {
|
||||
list_add_tail(&cmd->dev_exec_cmd_list_entry, &dev->dev_exec_cmd_list);
|
||||
cmd->on_dev_exec_list = 1;
|
||||
}
|
||||
|
||||
spin_lock_bh(&dev->dev_lock);
|
||||
/*
|
||||
* After a cmd passed SCSI atomicy check, there's no need to recheck SCSI
|
||||
* atomicity for this cmd in future entrances here, because then all
|
||||
* future overlapping with this cmd cmds will be blocked on it.
|
||||
*/
|
||||
|
||||
if (unlikely(((cmd->op_flags & SCST_SCSI_ATOMIC) != 0) ||
|
||||
(dev->dev_scsi_atomic_cmd_active != 0)) &&
|
||||
!cmd->scsi_atomicity_checked) {
|
||||
cmd->scsi_atomicity_checked = 1;
|
||||
if ((cmd->op_flags & SCST_SCSI_ATOMIC) != 0) {
|
||||
dev->dev_scsi_atomic_cmd_active++;
|
||||
TRACE_DBG("cmd %p (dev %p), scsi atomic_cmd_active %d",
|
||||
cmd, dev, dev->dev_scsi_atomic_cmd_active);
|
||||
}
|
||||
|
||||
res = scst_check_scsi_atomicity(cmd);
|
||||
if (res) {
|
||||
EXTRACHECKS_BUG_ON(dev->dev_scsi_atomic_cmd_active == 0);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
dev->on_dev_cmd_count++;
|
||||
cmd->dec_on_dev_needed = 1;
|
||||
@@ -140,8 +417,46 @@ static bool scst_check_blocked_dev(struct scst_cmd *cmd)
|
||||
cmd->dec_on_dev_needed = 0;
|
||||
TRACE_DBG("New dec on_dev_count %d (cmd %p)",
|
||||
dev->on_dev_cmd_count, cmd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* No locks. Returns true if cmd blocked, hence stop processing it and go to
|
||||
* the next command.
|
||||
*/
|
||||
static bool scst_check_blocked_dev(struct scst_cmd *cmd)
|
||||
{
|
||||
bool res;
|
||||
struct scst_device *dev = cmd->dev;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (unlikely((cmd->op_flags & SCST_CAN_GEN_3PARTY_COMMANDS) != 0)) {
|
||||
EXTRACHECKS_BUG_ON(cmd->cdb[0] != EXTENDED_COPY);
|
||||
res = scst_cm_check_block_all_devs(cmd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (unlikely(cmd->internal || cmd->bypass_blocking)) {
|
||||
/*
|
||||
* The original command can already block the device and must
|
||||
* hold reference to it, so internal command should always pass.
|
||||
*/
|
||||
|
||||
/* Copy Manager can send internal INQUIRYs, so don't BUG on them */
|
||||
sBUG_ON((dev->on_dev_cmd_count == 0) && (cmd->cdb[0] != INQUIRY));
|
||||
|
||||
res = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
spin_lock_bh(&dev->dev_lock);
|
||||
res = scst_do_check_blocked_dev(cmd);
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
|
||||
out:
|
||||
@@ -149,12 +464,68 @@ out:
|
||||
return res;
|
||||
}
|
||||
|
||||
/* No locks */
|
||||
static void scst_check_unblock_dev(struct scst_cmd *cmd)
|
||||
/* dev_lock supposed to be held and BH disabled */
|
||||
static void scst_check_unblock_scsi_atomic_cmds(struct scst_cmd *cmd)
|
||||
{
|
||||
int i;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
EXTRACHECKS_BUG_ON(cmd->scsi_atomic_blocked_cmds_count == 0);
|
||||
|
||||
for (i = 0; i < cmd->scsi_atomic_blocked_cmds_count; i++) {
|
||||
struct scst_cmd *acmd = cmd->scsi_atomic_blocked_cmds[i];
|
||||
|
||||
acmd->scsi_atomic_blockers--;
|
||||
if (acmd->scsi_atomic_blockers == 0) {
|
||||
TRACE_BLOCK("Unblocking blocked acmd %p (blocker "
|
||||
"cmd %p)", acmd, cmd);
|
||||
spin_lock_irq(&acmd->cmd_threads->cmd_list_lock);
|
||||
if (acmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE)
|
||||
list_add(&acmd->cmd_list_entry,
|
||||
&acmd->cmd_threads->active_cmd_list);
|
||||
else
|
||||
list_add_tail(&acmd->cmd_list_entry,
|
||||
&acmd->cmd_threads->active_cmd_list);
|
||||
wake_up(&acmd->cmd_threads->cmd_list_waitQ);
|
||||
spin_unlock_irq(&acmd->cmd_threads->cmd_list_lock);
|
||||
}
|
||||
}
|
||||
|
||||
kfree(cmd->scsi_atomic_blocked_cmds);
|
||||
cmd->scsi_atomic_blocked_cmds = NULL;
|
||||
cmd->scsi_atomic_blocked_cmds_count = 0;
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
/* dev_lock supposed to be BH locked */
|
||||
void __scst_check_unblock_dev(struct scst_cmd *cmd)
|
||||
{
|
||||
struct scst_device *dev = cmd->dev;
|
||||
|
||||
spin_lock_bh(&dev->dev_lock);
|
||||
TRACE_ENTRY();
|
||||
|
||||
/*
|
||||
* We might be called here as part of Copy Manager's check blocking
|
||||
* undo, so restore all flags in the previous state to allow
|
||||
* restart of this cmd.
|
||||
*/
|
||||
|
||||
if (likely(cmd->on_dev_exec_list)) {
|
||||
list_del(&cmd->dev_exec_cmd_list_entry);
|
||||
cmd->on_dev_exec_list = 0;
|
||||
}
|
||||
|
||||
if (unlikely((cmd->op_flags & SCST_SCSI_ATOMIC) != 0)) {
|
||||
if (likely(cmd->scsi_atomicity_checked)) {
|
||||
dev->dev_scsi_atomic_cmd_active--;
|
||||
TRACE_DBG("cmd %p, scsi atomic_cmd_active %d",
|
||||
cmd, dev->dev_scsi_atomic_cmd_active);
|
||||
cmd->scsi_atomicity_checked = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (likely(cmd->dec_on_dev_needed)) {
|
||||
dev->on_dev_cmd_count--;
|
||||
@@ -163,6 +534,9 @@ static void scst_check_unblock_dev(struct scst_cmd *cmd)
|
||||
dev->on_dev_cmd_count, cmd);
|
||||
}
|
||||
|
||||
if (unlikely(cmd->scsi_atomic_blocked_cmds != NULL))
|
||||
scst_check_unblock_scsi_atomic_cmds(cmd);
|
||||
|
||||
if (unlikely(cmd->unblock_dev)) {
|
||||
TRACE_BLOCK("cmd %p (tag %llu): unblocking dev %s", cmd,
|
||||
(unsigned long long int)cmd->tag, dev->virt_name);
|
||||
@@ -176,7 +550,30 @@ static void scst_check_unblock_dev(struct scst_cmd *cmd)
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(dev->ext_blocking_pending)) {
|
||||
if (dev->on_dev_cmd_count == 0) {
|
||||
TRACE_MGMT_DBG("Releasing pending dev %s extended "
|
||||
"blocks", dev->virt_name);
|
||||
scst_ext_blocking_done(dev);
|
||||
}
|
||||
}
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
/* No locks */
|
||||
void scst_check_unblock_dev(struct scst_cmd *cmd)
|
||||
{
|
||||
struct scst_device *dev = cmd->dev;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
spin_lock_bh(&dev->dev_lock);
|
||||
__scst_check_unblock_dev(cmd);
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -373,6 +770,7 @@ out_redirect:
|
||||
msleep(50);
|
||||
} else {
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&scst_init_lock, flags);
|
||||
TRACE_DBG("Adding cmd %p to init cmd list", cmd);
|
||||
list_add_tail(&cmd->cmd_list_entry, &scst_init_cmd_list);
|
||||
@@ -1008,6 +1406,7 @@ out_check_compl:
|
||||
if (unlikely(test_bit(SCST_TGT_DEV_BLACK_HOLE, &cmd->tgt_dev->tgt_dev_flags))) {
|
||||
struct scst_session *sess = cmd->sess;
|
||||
bool abort = false;
|
||||
|
||||
switch (sess->acg->acg_black_hole_type) {
|
||||
case SCST_ACG_BLACK_HOLE_CMD:
|
||||
case SCST_ACG_BLACK_HOLE_ALL:
|
||||
@@ -1410,6 +1809,7 @@ static int scst_rdy_to_xfer(struct scst_cmd *cmd)
|
||||
|
||||
if (tgtt->on_hw_pending_cmd_timeout != NULL) {
|
||||
struct scst_session *sess = cmd->sess;
|
||||
|
||||
cmd->hw_pending_start = jiffies;
|
||||
cmd->cmd_hw_pending = 1;
|
||||
if (!test_bit(SCST_SESS_HW_PENDING_WORK_SCHEDULED, &sess->sess_aflags)) {
|
||||
@@ -1669,6 +2069,7 @@ static int scst_tgt_pre_exec(struct scst_cmd *cmd)
|
||||
if (unlikely(cmd->resid_possible)) {
|
||||
if (cmd->data_direction & SCST_DATA_WRITE) {
|
||||
bool remainder = false;
|
||||
|
||||
if (cmd->data_direction & SCST_DATA_READ) {
|
||||
if (cmd->write_len != cmd->out_bufflen)
|
||||
remainder = true;
|
||||
@@ -1706,7 +2107,17 @@ static int scst_tgt_pre_exec(struct scst_cmd *cmd)
|
||||
|
||||
cmd->state = SCST_CMD_STATE_EXEC_CHECK_SN;
|
||||
|
||||
if ((cmd->tgtt->pre_exec == NULL) || unlikely(cmd->internal))
|
||||
if (unlikely(cmd->internal)) {
|
||||
if (cmd->dh_data_buf_alloced && cmd->tgt_i_data_buf_alloced &&
|
||||
(scst_cmd_get_data_direction(cmd) & SCST_DATA_WRITE)) {
|
||||
TRACE_DBG("Internal WRITE cmd %p with DH alloced data",
|
||||
cmd);
|
||||
scst_copy_sg(cmd, SCST_SG_COPY_FROM_TARGET);
|
||||
}
|
||||
goto out_descr;
|
||||
}
|
||||
|
||||
if (cmd->tgtt->pre_exec == NULL)
|
||||
goto out_descr;
|
||||
|
||||
TRACE_DBG("Calling pre_exec(%p)", cmd);
|
||||
@@ -1738,6 +2149,7 @@ static int scst_tgt_pre_exec(struct scst_cmd *cmd)
|
||||
out_descr:
|
||||
if (unlikely(cmd->op_flags & SCST_DESCRIPTORS_BASED)) {
|
||||
int r = scst_parse_descriptors(cmd);
|
||||
|
||||
if (unlikely(r != 0))
|
||||
goto out;
|
||||
}
|
||||
@@ -1889,7 +2301,7 @@ static int scst_report_luns_local(struct scst_cmd *cmd)
|
||||
cmd->driver_status = 0;
|
||||
|
||||
if ((cmd->cdb[2] != 0) && (cmd->cdb[2] != 2)) {
|
||||
TRACE(TRACE_MINOR, "Unsupported SELECT REPORT value %x in "
|
||||
TRACE(TRACE_MINOR, "Unsupported SELECT REPORT value %#x in "
|
||||
"REPORT LUNS command", cmd->cdb[2]);
|
||||
scst_set_invalid_field_in_cdb(cmd, 2, 0);
|
||||
goto out_compl;
|
||||
@@ -1910,6 +2322,7 @@ static int scst_report_luns_local(struct scst_cmd *cmd)
|
||||
rcu_read_lock();
|
||||
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
|
||||
struct list_head *head = &cmd->sess->sess_tgt_dev_list[i];
|
||||
|
||||
list_for_each_entry_rcu(tgt_dev, head,
|
||||
sess_tgt_dev_list_entry) {
|
||||
struct scst_tgt_dev_UA *ua;
|
||||
@@ -2740,7 +3153,7 @@ out_unlock:
|
||||
scst_put_buf_full(cmd, buffer);
|
||||
|
||||
out_done:
|
||||
if (SCST_EXEC_COMPLETED == res) {
|
||||
if (res == SCST_EXEC_COMPLETED) {
|
||||
if (!aborted)
|
||||
cmd->completed = 1;
|
||||
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT,
|
||||
@@ -2770,7 +3183,7 @@ int __scst_check_local_events(struct scst_cmd *cmd, bool preempt_tests_only)
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (unlikely(cmd->internal)) {
|
||||
if (unlikely(cmd->internal && !cmd->internal_check_local_events)) {
|
||||
if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))) {
|
||||
TRACE_MGMT_DBG("ABORTED set, aborting internal "
|
||||
"cmd %p", cmd);
|
||||
@@ -2975,6 +3388,7 @@ static struct scst_cmd *scst_post_exec_sn(struct scst_cmd *cmd,
|
||||
|
||||
if (inc_expected_sn) {
|
||||
bool rc = scst_inc_expected_sn(cmd);
|
||||
|
||||
if (!rc)
|
||||
goto out;
|
||||
if (make_active)
|
||||
@@ -3008,7 +3422,7 @@ static int scst_do_real_exec(struct scst_cmd *cmd)
|
||||
if (devt->exec) {
|
||||
TRACE_DBG("Calling dev handler %s exec(%p)",
|
||||
devt->name, cmd);
|
||||
scst_set_cur_start(cmd);
|
||||
scst_set_exec_start(cmd);
|
||||
res = devt->exec(cmd);
|
||||
TRACE_DBG("Dev handler %s exec() returned %d",
|
||||
devt->name, res);
|
||||
@@ -3034,7 +3448,7 @@ static int scst_do_real_exec(struct scst_cmd *cmd)
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
scst_set_cur_start(cmd);
|
||||
scst_set_exec_start(cmd);
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
|
||||
rc = scst_exec_req(scsi_dev, cmd->cdb, cmd->cdb_len,
|
||||
@@ -3127,6 +3541,9 @@ static scst_local_exec_fn scst_local_fns[256] = {
|
||||
[PERSISTENT_RESERVE_OUT] = scst_persistent_reserve_out_local,
|
||||
[REPORT_LUNS] = scst_report_luns_local,
|
||||
[REQUEST_SENSE] = scst_request_sense_local,
|
||||
[COMPARE_AND_WRITE] = scst_cmp_wr_local,
|
||||
[EXTENDED_COPY] = scst_cm_ext_copy_exec,
|
||||
[RECEIVE_COPY_RESULTS] = scst_cm_rcv_copy_res_exec,
|
||||
[MAINTENANCE_IN] = scst_maintenance_in,
|
||||
};
|
||||
|
||||
@@ -3229,15 +3646,41 @@ out_done:
|
||||
goto out;
|
||||
}
|
||||
|
||||
static inline bool scst_check_alua(struct scst_cmd *cmd, int *out_res)
|
||||
{
|
||||
int (*alua_filter)(struct scst_cmd *cmd);
|
||||
bool res = false;
|
||||
|
||||
alua_filter = ACCESS_ONCE(cmd->tgt_dev->alua_filter);
|
||||
if (unlikely(alua_filter)) {
|
||||
int ac = alua_filter(cmd);
|
||||
|
||||
if (ac != SCST_ALUA_CHECK_OK) {
|
||||
if (ac != SCST_ALUA_CHECK_DELAYED) {
|
||||
EXTRACHECKS_BUG_ON(cmd->status == 0);
|
||||
scst_set_cmd_abnormal_done_state(cmd);
|
||||
*out_res = SCST_CMD_STATE_RES_CONT_SAME;
|
||||
}
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int scst_exec_check_blocking(struct scst_cmd **active_cmd)
|
||||
{
|
||||
struct scst_cmd *cmd = *active_cmd;
|
||||
struct scst_cmd *ref_cmd;
|
||||
int res = SCST_CMD_STATE_RES_CONT_NEXT;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
cmd->state = SCST_CMD_STATE_EXEC_CHECK_BLOCKING;
|
||||
|
||||
if (unlikely(scst_check_alua(cmd, &res)))
|
||||
goto out;
|
||||
|
||||
if (unlikely(scst_check_blocked_dev(cmd)))
|
||||
goto out;
|
||||
|
||||
@@ -3251,6 +3694,7 @@ static int scst_exec_check_blocking(struct scst_cmd **active_cmd)
|
||||
#ifdef CONFIG_SCST_DEBUG_SN
|
||||
if ((scst_random() % 120) == 7) {
|
||||
int t = scst_random() % 200;
|
||||
|
||||
TRACE_SN("Delaying IO on %d ms", t);
|
||||
msleep(t);
|
||||
}
|
||||
@@ -3280,9 +3724,9 @@ static int scst_exec_check_blocking(struct scst_cmd **active_cmd)
|
||||
cmd->state = SCST_CMD_STATE_LOCAL_EXEC;
|
||||
|
||||
rc = scst_do_local_exec(cmd);
|
||||
if (likely(rc == SCST_EXEC_NOT_COMPLETED))
|
||||
/* Nothing to do */;
|
||||
else {
|
||||
if (likely(rc == SCST_EXEC_NOT_COMPLETED)) {
|
||||
/* Nothing to do */
|
||||
} else {
|
||||
sBUG_ON(rc != SCST_EXEC_COMPLETED);
|
||||
goto done;
|
||||
}
|
||||
@@ -3301,6 +3745,9 @@ done:
|
||||
|
||||
cmd->state = SCST_CMD_STATE_EXEC_CHECK_BLOCKING;
|
||||
|
||||
if (unlikely(scst_check_alua(cmd, &res)))
|
||||
goto out;
|
||||
|
||||
if (unlikely(scst_check_blocked_dev(cmd)))
|
||||
break;
|
||||
|
||||
@@ -3320,8 +3767,8 @@ done:
|
||||
/* !! At this point sess, dev and tgt_dev can be already freed !! */
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return SCST_CMD_STATE_RES_CONT_NEXT;
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int scst_exec_check_sn(struct scst_cmd **active_cmd)
|
||||
@@ -3421,6 +3868,7 @@ static int scst_check_sense(struct scst_cmd *cmd)
|
||||
} else {
|
||||
int sl;
|
||||
uint8_t sense[SCST_STANDARD_SENSE_LEN];
|
||||
|
||||
TRACE(TRACE_MGMT, "DID_RESET received for device %s, "
|
||||
"triggering reset UA", dev->virt_name);
|
||||
sl = scst_set_sense(sense, sizeof(sense), dev->d_sense,
|
||||
@@ -3597,6 +4045,7 @@ next:
|
||||
if (likely(scst_cmd_completed_good(cmd))) {
|
||||
if (cmd->deferred_dif_read_check) {
|
||||
int rc = scst_dif_process_read(cmd);
|
||||
|
||||
if (unlikely(rc != 0)) {
|
||||
cmd->deferred_dif_read_check = 0;
|
||||
goto again;
|
||||
@@ -3709,6 +4158,7 @@ static int scst_mode_select_checks(struct scst_cmd *cmd)
|
||||
|
||||
if (likely(scsi_status_is_good(cmd->status))) {
|
||||
int atomic = scst_cmd_atomic(cmd);
|
||||
|
||||
if (unlikely((cmd->cdb[0] == MODE_SELECT) ||
|
||||
(cmd->cdb[0] == MODE_SELECT_10) ||
|
||||
(cmd->cdb[0] == LOG_SELECT))) {
|
||||
@@ -3762,6 +4212,7 @@ static int scst_mode_select_checks(struct scst_cmd *cmd)
|
||||
SCST_SENSE_ASC_VALID,
|
||||
0, 0x2F, 0))) {
|
||||
int atomic = scst_cmd_atomic(cmd);
|
||||
|
||||
if (atomic) {
|
||||
TRACE_DBG("Possible parameters changed UA %x: "
|
||||
"thread context required", cmd->sense[12]);
|
||||
@@ -3871,6 +4322,7 @@ static int scst_dev_done(struct scst_cmd *cmd)
|
||||
|
||||
if (cmd->inc_expected_sn_on_done && cmd->sent_for_exec && cmd->sn_set) {
|
||||
bool rc = scst_inc_expected_sn(cmd);
|
||||
|
||||
if (rc)
|
||||
scst_make_deferred_commands_active(cmd->cur_order_data);
|
||||
}
|
||||
@@ -4035,6 +4487,7 @@ static int scst_xmit_response(struct scst_cmd *cmd)
|
||||
(cmd->data_direction & SCST_DATA_READ)) {
|
||||
int i, sg_cnt;
|
||||
struct scatterlist *sg, *sgi;
|
||||
|
||||
if (cmd->tgt_i_sg != NULL) {
|
||||
sg = cmd->tgt_i_sg;
|
||||
sg_cnt = cmd->tgt_i_sg_cnt;
|
||||
@@ -4059,6 +4512,7 @@ static int scst_xmit_response(struct scst_cmd *cmd)
|
||||
|
||||
if (tgtt->on_hw_pending_cmd_timeout != NULL) {
|
||||
struct scst_session *sess = cmd->sess;
|
||||
|
||||
cmd->hw_pending_start = jiffies;
|
||||
cmd->cmd_hw_pending = 1;
|
||||
if (!test_bit(SCST_SESS_HW_PENDING_WORK_SCHEDULED, &sess->sess_aflags)) {
|
||||
@@ -4216,6 +4670,12 @@ static int scst_finish_cmd(struct scst_cmd *cmd)
|
||||
|
||||
spin_unlock_irq(&sess->sess_list_lock);
|
||||
|
||||
if (unlikely(cmd->cmd_on_global_stpg_list)) {
|
||||
TRACE_DBG("Unlisting being freed STPG cmd %p", cmd);
|
||||
EXTRACHECKS_BUG_ON(cmd->cmd_global_stpg_blocked);
|
||||
scst_stpg_del_unblock_next(cmd);
|
||||
}
|
||||
|
||||
if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)))
|
||||
scst_finish_cmd_mgmt(cmd);
|
||||
|
||||
@@ -4313,6 +4773,7 @@ again:
|
||||
order_data->cur_sn_slot = order_data->sn_slots;
|
||||
if (unlikely(atomic_read(order_data->cur_sn_slot) != 0)) {
|
||||
static int q;
|
||||
|
||||
if (q++ < 10)
|
||||
PRINT_WARNING("Not enough SN slots "
|
||||
"(dev %s)", cmd->dev->virt_name);
|
||||
@@ -4487,6 +4948,7 @@ static int scst_translate_lun(struct scst_cmd *cmd)
|
||||
"unexisting LU (initiator %s, target %s)?",
|
||||
(unsigned long long int)cmd->lun,
|
||||
cmd->sess->initiator_name, cmd->tgt->tgt_name);
|
||||
scst_event_queue_lun_not_found(cmd);
|
||||
}
|
||||
scst_put(cmd->cpu_cmd_counter);
|
||||
}
|
||||
@@ -4508,7 +4970,6 @@ static int scst_translate_lun(struct scst_cmd *cmd)
|
||||
*/
|
||||
static int __scst_init_cmd(struct scst_cmd *cmd)
|
||||
{
|
||||
bool (*alua_filter)(struct scst_cmd *cmd);
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
@@ -4549,12 +5010,6 @@ static int __scst_init_cmd(struct scst_cmd *cmd)
|
||||
if (unlikely(failure))
|
||||
goto out_busy;
|
||||
|
||||
alua_filter = ACCESS_ONCE(cmd->tgt_dev->alua_filter);
|
||||
if (unlikely(alua_filter && !alua_filter(cmd))) {
|
||||
scst_set_cmd_abnormal_done_state(cmd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* SCST_IMPLICIT_HQ for unknown commands not implemented for
|
||||
* case when set_sn_on_restart_cmd not set, because custom parse
|
||||
@@ -4571,6 +5026,7 @@ static int __scst_init_cmd(struct scst_cmd *cmd)
|
||||
else {
|
||||
struct scst_order_data *order_data = cmd->cur_order_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&order_data->init_done_lock, flags);
|
||||
scst_cmd_set_sn(cmd);
|
||||
spin_unlock_irqrestore(&order_data->init_done_lock, flags);
|
||||
@@ -4615,6 +5071,7 @@ restart:
|
||||
|
||||
list_for_each_entry(cmd, &scst_init_cmd_list, cmd_list_entry) {
|
||||
int rc;
|
||||
|
||||
if (susp && !test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))
|
||||
continue;
|
||||
if (!test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)) {
|
||||
@@ -5381,6 +5838,9 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
/* Fantom EC commands must not leak here */
|
||||
sBUG_ON((cmd->cdb[0] == EXTENDED_COPY) && cmd->internal);
|
||||
|
||||
/*
|
||||
* Help Coverity recognize that mcmd != NULL if
|
||||
* call_dev_task_mgmt_fn_received == true.
|
||||
@@ -5430,6 +5890,9 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
|
||||
*/
|
||||
smp_mb__after_set_bit();
|
||||
|
||||
if (cmd->cdb[0] == EXTENDED_COPY)
|
||||
scst_cm_abort_ec_cmd(cmd);
|
||||
|
||||
if (cmd->tgt_dev == NULL) {
|
||||
spin_lock_irqsave(&scst_init_lock, flags);
|
||||
scst_init_poll_cnt++;
|
||||
@@ -5488,6 +5951,7 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
|
||||
if (mstb->done_counted || mstb->finish_counted) {
|
||||
unsigned long t;
|
||||
char state_name[32];
|
||||
|
||||
if (mcmd->fn != SCST_PR_ABORT_ALL)
|
||||
t = TRACE_MGMT;
|
||||
else
|
||||
@@ -5575,6 +6039,7 @@ static int scst_set_mcmd_next_state(struct scst_mgmt_cmd *mcmd)
|
||||
default:
|
||||
{
|
||||
char fn_name[16], state_name[32];
|
||||
|
||||
PRINT_CRIT_ERROR("Wrong mcmd %p state %s (fn %s, "
|
||||
"cmd_finish_wait_count %d, cmd_done_wait_count %d)",
|
||||
mcmd, scst_get_mcmd_state_name(state_name,
|
||||
@@ -5596,11 +6061,14 @@ static int scst_set_mcmd_next_state(struct scst_mgmt_cmd *mcmd)
|
||||
|
||||
/* IRQs supposed to be disabled */
|
||||
static bool __scst_check_unblock_aborted_cmd(struct scst_cmd *cmd,
|
||||
struct list_head *list_entry)
|
||||
struct list_head *list_entry, bool blocked)
|
||||
{
|
||||
bool res;
|
||||
|
||||
if (test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)) {
|
||||
list_del(list_entry);
|
||||
if (blocked)
|
||||
cmd->cmd_global_stpg_blocked = 0;
|
||||
spin_lock(&cmd->cmd_threads->cmd_list_lock);
|
||||
list_add_tail(&cmd->cmd_list_entry,
|
||||
&cmd->cmd_threads->active_cmd_list);
|
||||
@@ -5643,7 +6111,7 @@ void scst_unblock_aborted_cmds(const struct scst_tgt *tgt,
|
||||
continue;
|
||||
|
||||
if (__scst_check_unblock_aborted_cmd(cmd,
|
||||
&cmd->blocked_cmd_list_entry)) {
|
||||
&cmd->blocked_cmd_list_entry, true)) {
|
||||
TRACE_MGMT_DBG("Unblock aborted blocked cmd %p", cmd);
|
||||
}
|
||||
}
|
||||
@@ -5654,6 +6122,7 @@ void scst_unblock_aborted_cmds(const struct scst_tgt *tgt,
|
||||
list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list,
|
||||
dev_tgt_dev_list_entry) {
|
||||
struct scst_order_data *order_data = tgt_dev->curr_order_data;
|
||||
|
||||
spin_lock(&order_data->sn_lock);
|
||||
list_for_each_entry_safe(cmd, tcmd,
|
||||
&order_data->deferred_cmd_list,
|
||||
@@ -5665,7 +6134,7 @@ void scst_unblock_aborted_cmds(const struct scst_tgt *tgt,
|
||||
continue;
|
||||
|
||||
if (__scst_check_unblock_aborted_cmd(cmd,
|
||||
&cmd->deferred_cmd_list_entry)) {
|
||||
&cmd->deferred_cmd_list_entry, false)) {
|
||||
TRACE_MGMT_DBG("Unblocked aborted SN "
|
||||
"cmd %p (sn %u)", cmd, cmd->sn);
|
||||
order_data->def_cmd_count--;
|
||||
@@ -5872,8 +6341,10 @@ static int scst_clear_task_set(struct scst_mgmt_cmd *mcmd)
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Returns 0 if the command processing should be continued,
|
||||
* >0, if it should be requeued, <0 otherwise */
|
||||
/*
|
||||
* Returns 0 if the command processing should be continued,
|
||||
* >0, if it should be requeued, <0 otherwise.
|
||||
*/
|
||||
static int scst_mgmt_cmd_init(struct scst_mgmt_cmd *mcmd)
|
||||
{
|
||||
int res = 0, rc, t;
|
||||
@@ -5966,6 +6437,8 @@ static int scst_mgmt_cmd_init(struct scst_mgmt_cmd *mcmd)
|
||||
}
|
||||
|
||||
out:
|
||||
scst_event_queue_tm_fn_received(mcmd);
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
@@ -6152,6 +6625,7 @@ static void scst_do_nexus_loss_sess(struct scst_mgmt_cmd *mcmd)
|
||||
rcu_read_lock();
|
||||
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
|
||||
struct list_head *head = &sess->sess_tgt_dev_list[i];
|
||||
|
||||
list_for_each_entry_rcu(tgt_dev, head,
|
||||
sess_tgt_dev_list_entry) {
|
||||
scst_nexus_loss(tgt_dev,
|
||||
@@ -6186,6 +6660,7 @@ static int scst_abort_all_nexus_loss_sess(struct scst_mgmt_cmd *mcmd,
|
||||
rcu_read_lock();
|
||||
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
|
||||
struct list_head *head = &sess->sess_tgt_dev_list[i];
|
||||
|
||||
list_for_each_entry_rcu(tgt_dev, head,
|
||||
sess_tgt_dev_list_entry) {
|
||||
__scst_abort_task_set(mcmd, tgt_dev);
|
||||
@@ -6220,6 +6695,7 @@ static void scst_do_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd)
|
||||
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
|
||||
struct list_head *head = &sess->sess_tgt_dev_list[i];
|
||||
struct scst_tgt_dev *tgt_dev;
|
||||
|
||||
list_for_each_entry_rcu(tgt_dev, head,
|
||||
sess_tgt_dev_list_entry) {
|
||||
scst_nexus_loss(tgt_dev, true);
|
||||
@@ -6257,6 +6733,7 @@ static int scst_abort_all_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd,
|
||||
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
|
||||
struct list_head *head = &sess->sess_tgt_dev_list[i];
|
||||
struct scst_tgt_dev *tgt_dev;
|
||||
|
||||
list_for_each_entry_rcu(tgt_dev, head,
|
||||
sess_tgt_dev_list_entry) {
|
||||
__scst_abort_task_set(mcmd, tgt_dev);
|
||||
@@ -6470,6 +6947,7 @@ static int scst_mgmt_affected_cmds_done(struct scst_mgmt_cmd *mcmd)
|
||||
rcu_read_lock();
|
||||
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
|
||||
struct list_head *head = &sess->sess_tgt_dev_list[i];
|
||||
|
||||
list_for_each_entry_rcu(tgt_dev, head, sess_tgt_dev_list_entry) {
|
||||
scst_call_dev_task_mgmt_fn_done(mcmd, tgt_dev);
|
||||
}
|
||||
@@ -6489,6 +6967,7 @@ static int scst_mgmt_affected_cmds_done(struct scst_mgmt_cmd *mcmd)
|
||||
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
|
||||
struct list_head *head = &s->sess_tgt_dev_list[i];
|
||||
struct scst_tgt_dev *tgt_dev;
|
||||
|
||||
list_for_each_entry_rcu(tgt_dev, head,
|
||||
sess_tgt_dev_list_entry) {
|
||||
if (mcmd->sess == tgt_dev->sess)
|
||||
@@ -6511,6 +6990,16 @@ static int scst_mgmt_affected_cmds_done(struct scst_mgmt_cmd *mcmd)
|
||||
tgt_done:
|
||||
scst_call_task_mgmt_affected_cmds_done(mcmd);
|
||||
|
||||
switch (mcmd->fn) {
|
||||
case SCST_LUN_RESET:
|
||||
case SCST_TARGET_RESET:
|
||||
case SCST_NEXUS_LOSS_SESS:
|
||||
case SCST_NEXUS_LOSS:
|
||||
case SCST_UNREG_SESS_TM:
|
||||
scst_cm_free_pending_list_ids(sess);
|
||||
break;
|
||||
}
|
||||
|
||||
res = scst_set_mcmd_next_state(mcmd);
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
@@ -6631,6 +7120,7 @@ static int scst_process_mgmt_cmd(struct scst_mgmt_cmd *mcmd)
|
||||
default:
|
||||
{
|
||||
char fn_name[16], state_name[32];
|
||||
|
||||
PRINT_CRIT_ERROR("Wrong mcmd %p state %s (fn %s, "
|
||||
"cmd_finish_wait_count %d, cmd_done_wait_count "
|
||||
"%d)", mcmd, scst_get_mcmd_state_name(state_name,
|
||||
@@ -6674,6 +7164,7 @@ int scst_tm_thread(void *arg)
|
||||
while (!list_empty(&scst_active_mgmt_cmd_list)) {
|
||||
int rc;
|
||||
struct scst_mgmt_cmd *mcmd;
|
||||
|
||||
mcmd = list_first_entry(&scst_active_mgmt_cmd_list,
|
||||
typeof(*mcmd), mgmt_cmd_list_entry);
|
||||
TRACE_MGMT_DBG("Deleting mgmt cmd %p from active cmd "
|
||||
@@ -7107,6 +7598,9 @@ bool scst_initiator_has_luns(struct scst_tgt *tgt, const char *initiator_name)
|
||||
|
||||
res = !list_empty(&acg->acg_dev_list);
|
||||
|
||||
if (!res)
|
||||
scst_event_queue_negative_luns_inquiry(tgt, initiator_name);
|
||||
|
||||
mutex_unlock(&scst_mutex);
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
@@ -7669,6 +8163,7 @@ struct scst_cmd *scst_find_cmd_by_tag(struct scst_session *sess,
|
||||
{
|
||||
unsigned long flags;
|
||||
struct scst_cmd *cmd;
|
||||
|
||||
spin_lock_irqsave(&sess->sess_list_lock, flags);
|
||||
cmd = __scst_find_cmd_by_tag(sess, tag, false);
|
||||
spin_unlock_irqrestore(&sess->sess_list_lock, flags);
|
||||
|
||||
+653
-67
@@ -16,13 +16,18 @@
|
||||
*/
|
||||
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <asm/unaligned.h>
|
||||
#ifdef INSIDE_KERNEL_TREE
|
||||
#include <scst/scst.h>
|
||||
#include <scst/scst_event.h>
|
||||
#else
|
||||
#include "scst.h"
|
||||
#include "scst_event.h"
|
||||
#endif
|
||||
#include "scst_priv.h"
|
||||
#include "scst_pres.h"
|
||||
|
||||
struct alua_state_and_name {
|
||||
enum scst_tg_state s;
|
||||
@@ -65,6 +70,7 @@ const char *scst_alua_state_name(enum scst_tg_state s)
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(scst_alua_state_name);
|
||||
|
||||
enum scst_tg_state scst_alua_name_to_state(const char *n)
|
||||
{
|
||||
@@ -127,8 +133,8 @@ static struct scst_tg_tgt *__lookup_dg_tgt(struct scst_dev_group *dg,
|
||||
}
|
||||
|
||||
/* Look up a target group by name in the given device group. */
|
||||
static struct scst_target_group *
|
||||
__lookup_tg_by_name(struct scst_dev_group *dg, const char *name)
|
||||
static struct scst_target_group *__lookup_tg_by_name(struct scst_dev_group *dg,
|
||||
const char *name)
|
||||
{
|
||||
struct scst_target_group *tg;
|
||||
|
||||
@@ -141,9 +147,24 @@ __lookup_tg_by_name(struct scst_dev_group *dg, const char *name)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Look up a target group by group ID. */
|
||||
static struct scst_target_group *__lookup_tg_by_group_id(struct scst_dev_group *dg,
|
||||
uint16_t group_id)
|
||||
{
|
||||
struct scst_target_group *tg;
|
||||
|
||||
lockdep_assert_held(&scst_mutex);
|
||||
|
||||
list_for_each_entry(tg, &dg->tg_list, entry)
|
||||
if (tg->group_id == group_id)
|
||||
return tg;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Look up a target group by target port. */
|
||||
static struct scst_target_group *
|
||||
__lookup_tg_by_tgt(struct scst_dev_group *dg, const struct scst_tgt *tgt)
|
||||
static struct scst_target_group *__lookup_tg_by_tgt(struct scst_dev_group *dg,
|
||||
const struct scst_tgt *tgt)
|
||||
{
|
||||
struct scst_target_group *tg;
|
||||
struct scst_tg_tgt *tg_tgt;
|
||||
@@ -253,11 +274,14 @@ static struct kobj_type scst_tg_tgt_ktype = {
|
||||
};
|
||||
|
||||
/*
|
||||
* Whether or not to accept a command in the ALUA unavailable and transitioning
|
||||
* states.
|
||||
* Whether or not to accept a command in the ALUA standby state.
|
||||
*/
|
||||
static bool scst_tg_accept(struct scst_cmd *cmd)
|
||||
static int scst_tg_accept_standby(struct scst_cmd *cmd)
|
||||
{
|
||||
int res;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
switch (cmd->cdb[0]) {
|
||||
case TEST_UNIT_READY:
|
||||
case GET_EVENT_STATUS_NOTIFICATION:
|
||||
@@ -273,41 +297,6 @@ static bool scst_tg_accept(struct scst_cmd *cmd)
|
||||
case RESERVE_10:
|
||||
case READ_BUFFER:
|
||||
case WRITE_BUFFER:
|
||||
return true;
|
||||
case SERVICE_ACTION_IN_16:
|
||||
switch (cmd->cdb[1] & 0x1f) {
|
||||
case SAI_READ_CAPACITY_16:
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case MAINTENANCE_IN:
|
||||
switch (cmd->cdb[1] & 0x1f) {
|
||||
case MI_REPORT_TARGET_PGS:
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case MAINTENANCE_OUT:
|
||||
switch (cmd->cdb[1] & 0x1f) {
|
||||
case MO_SET_TARGET_PGS:
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Whether or not to accept a command in the ALUA standby state.
|
||||
*/
|
||||
static bool scst_tg_accept_standby(struct scst_cmd *cmd)
|
||||
{
|
||||
bool process_cmd = scst_tg_accept(cmd);
|
||||
|
||||
if (process_cmd)
|
||||
return process_cmd;
|
||||
|
||||
switch (cmd->cdb[0]) {
|
||||
case MODE_SELECT:
|
||||
case MODE_SELECT_10:
|
||||
case LOG_SELECT:
|
||||
@@ -316,43 +305,187 @@ static bool scst_tg_accept_standby(struct scst_cmd *cmd)
|
||||
case SEND_DIAGNOSTIC:
|
||||
case PERSISTENT_RESERVE_IN:
|
||||
case PERSISTENT_RESERVE_OUT:
|
||||
return true;
|
||||
res = SCST_ALUA_CHECK_OK;
|
||||
goto out;
|
||||
case SERVICE_ACTION_IN_16:
|
||||
switch (cmd->cdb[1] & 0x1f) {
|
||||
case SAI_READ_CAPACITY_16:
|
||||
res = SCST_ALUA_CHECK_OK;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case MAINTENANCE_IN:
|
||||
switch (cmd->cdb[1] & 0x1f) {
|
||||
case MI_REPORT_TARGET_PGS:
|
||||
res = SCST_ALUA_CHECK_OK;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case MAINTENANCE_OUT:
|
||||
switch (cmd->cdb[1] & 0x1f) {
|
||||
case MO_SET_TARGET_PGS:
|
||||
res = SCST_ALUA_CHECK_OK;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_tp_standby));
|
||||
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_alua_standby));
|
||||
res = SCST_ALUA_CHECK_ERROR;
|
||||
|
||||
return false;
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Whether or not to accept a command in the ALUA unavailable state.
|
||||
*/
|
||||
static bool scst_tg_accept_unav(struct scst_cmd *cmd)
|
||||
static int scst_tg_accept_unav(struct scst_cmd *cmd)
|
||||
{
|
||||
bool process_cmd = scst_tg_accept(cmd);
|
||||
int res;
|
||||
|
||||
if (!process_cmd)
|
||||
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_tp_unav));
|
||||
TRACE_ENTRY();
|
||||
|
||||
return process_cmd;
|
||||
switch (cmd->cdb[0]) {
|
||||
case TEST_UNIT_READY:
|
||||
case GET_EVENT_STATUS_NOTIFICATION:
|
||||
case INQUIRY:
|
||||
case MODE_SENSE:
|
||||
case MODE_SENSE_10:
|
||||
case READ_CAPACITY:
|
||||
case REPORT_LUNS:
|
||||
case REQUEST_SENSE:
|
||||
case RELEASE:
|
||||
case RELEASE_10:
|
||||
case RESERVE:
|
||||
case RESERVE_10:
|
||||
case READ_BUFFER:
|
||||
case WRITE_BUFFER:
|
||||
res = SCST_ALUA_CHECK_OK;
|
||||
goto out;
|
||||
case SERVICE_ACTION_IN_16:
|
||||
switch (cmd->cdb[1] & 0x1f) {
|
||||
case SAI_READ_CAPACITY_16:
|
||||
res = SCST_ALUA_CHECK_OK;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case MAINTENANCE_IN:
|
||||
switch (cmd->cdb[1] & 0x1f) {
|
||||
case MI_REPORT_TARGET_PGS:
|
||||
res = SCST_ALUA_CHECK_OK;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case MAINTENANCE_OUT:
|
||||
switch (cmd->cdb[1] & 0x1f) {
|
||||
case MO_SET_TARGET_PGS:
|
||||
res = SCST_ALUA_CHECK_OK;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_alua_unav));
|
||||
res = SCST_ALUA_CHECK_ERROR;
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
struct scst_alua_retry {
|
||||
struct scst_cmd *alua_retry_cmd;
|
||||
struct delayed_work alua_retry_work;
|
||||
};
|
||||
|
||||
static void scst_alua_transitioning_work_fn(struct delayed_work *work)
|
||||
{
|
||||
struct scst_alua_retry *retry = container_of(work, struct scst_alua_retry,
|
||||
alua_retry_work);
|
||||
struct scst_cmd *cmd = retry->alua_retry_cmd;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_DBG("Retrying transitioning cmd %p", cmd);
|
||||
|
||||
spin_lock_irq(&cmd->cmd_threads->cmd_list_lock);
|
||||
list_add(&cmd->cmd_list_entry,
|
||||
&cmd->cmd_threads->active_cmd_list);
|
||||
wake_up(&cmd->cmd_threads->cmd_list_waitQ);
|
||||
spin_unlock_irq(&cmd->cmd_threads->cmd_list_lock);
|
||||
|
||||
kfree(retry);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Whether or not to accept a command in the ALUA transitioning state.
|
||||
*/
|
||||
static bool scst_tg_accept_transitioning(struct scst_cmd *cmd)
|
||||
static int scst_tg_accept_transitioning(struct scst_cmd *cmd)
|
||||
{
|
||||
bool process_cmd = scst_tg_accept(cmd);
|
||||
int res;
|
||||
|
||||
if (!process_cmd)
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_tp_transitioning));
|
||||
TRACE_ENTRY();
|
||||
|
||||
return process_cmd;
|
||||
switch (cmd->cdb[0]) {
|
||||
case INQUIRY:
|
||||
case READ_CAPACITY:
|
||||
case REPORT_LUNS:
|
||||
case REQUEST_SENSE:
|
||||
case READ_BUFFER:
|
||||
case WRITE_BUFFER:
|
||||
res = SCST_ALUA_CHECK_OK;
|
||||
goto out;
|
||||
case SERVICE_ACTION_IN_16:
|
||||
switch (cmd->cdb[1] & 0x1f) {
|
||||
case SAI_READ_CAPACITY_16:
|
||||
res = SCST_ALUA_CHECK_OK;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (cmd->already_transitioning)
|
||||
TRACE_DBG("cmd %p already transitioned checked, failing", cmd);
|
||||
else {
|
||||
struct scst_alua_retry *retry;
|
||||
|
||||
TRACE_DBG("ALUA transitioning: delaying cmd %p", cmd);
|
||||
|
||||
retry = kzalloc(sizeof(*retry), GFP_KERNEL);
|
||||
if (retry == NULL) {
|
||||
TRACE_DBG("Unable to allocate ALUA retry "
|
||||
"struct, failing cmd %p", cmd);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_alua_transitioning));
|
||||
res = SCST_ALUA_CHECK_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* No get is needed, because cmd is sync here */
|
||||
retry->alua_retry_cmd = cmd;
|
||||
INIT_DELAYED_WORK(&retry->alua_retry_work,
|
||||
(void (*)(struct work_struct *))scst_alua_transitioning_work_fn);
|
||||
cmd->already_transitioning = 1;
|
||||
schedule_delayed_work(&retry->alua_retry_work, HZ/2);
|
||||
res = SCST_ALUA_CHECK_DELAYED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_alua_transitioning));
|
||||
res = SCST_ALUA_CHECK_ERROR;
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool (*scst_alua_filter[])(struct scst_cmd *cmd) = {
|
||||
static int (*scst_alua_filter[])(struct scst_cmd *cmd) = {
|
||||
[SCST_TG_STATE_OPTIMIZED] = NULL,
|
||||
[SCST_TG_STATE_NONOPTIMIZED] = NULL,
|
||||
[SCST_TG_STATE_STANDBY] = scst_tg_accept_standby,
|
||||
@@ -424,7 +557,9 @@ static void scst_tg_change_tgt_dev_state(struct scst_tgt_dev *tgt_dev,
|
||||
{
|
||||
lockdep_assert_held(&scst_dg_mutex);
|
||||
|
||||
TRACE_MGMT_DBG("ALUA state of tgt_dev %p has changed", tgt_dev);
|
||||
TRACE_MGMT_DBG("ALUA state of tgt_dev %p has changed (gen_ua %d)",
|
||||
tgt_dev, gen_ua);
|
||||
|
||||
scst_update_tgt_dev_alua_filter(tgt_dev, state);
|
||||
if (gen_ua)
|
||||
scst_gen_aen_or_ua(tgt_dev,
|
||||
@@ -724,20 +859,96 @@ out:
|
||||
return res;
|
||||
}
|
||||
|
||||
static void scst_event_stpg_notify_fn(struct scst_event *event,
|
||||
void *priv, int status)
|
||||
{
|
||||
struct scst_dev_group *dg;
|
||||
struct scst_cmd *cmd = (struct scst_cmd *)priv;
|
||||
struct scst_event_stpg_payload *p =
|
||||
(struct scst_event_stpg_payload *)event->payload;
|
||||
struct scst_event_stpg_descr *d;
|
||||
struct scst_dg_dev *dgd;
|
||||
int i;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
PRINT_INFO("Notification for event %u (id %d) received "
|
||||
"with status %d (priv %p)", event->event_code,
|
||||
event->event_id, status, priv);
|
||||
|
||||
mutex_lock(&scst_mutex);
|
||||
mutex_lock(&scst_dg_mutex);
|
||||
|
||||
dg = __lookup_dg_by_dev(cmd->dev);
|
||||
if (!dg) {
|
||||
PRINT_ERROR("STPG: unable to find DG for device %s",
|
||||
cmd->dev->virt_name);
|
||||
goto out_fail;
|
||||
}
|
||||
|
||||
list_for_each_entry(dgd, &dg->dev_list, entry) {
|
||||
if (dgd->dev->stpg_ext_blocked) {
|
||||
TRACE_DBG("STPG: ext unblocking dev %s",
|
||||
dgd->dev->virt_name);
|
||||
scst_ext_unblock_dev(dgd->dev, true);
|
||||
dgd->dev->stpg_ext_blocked = 0;
|
||||
}
|
||||
}
|
||||
|
||||
kfree(dg->stpg_transport_id);
|
||||
dg->stpg_transport_id = NULL;
|
||||
|
||||
if (status != 0) {
|
||||
PRINT_ERROR("on_stpg script for device group %s failed with status %d",
|
||||
dg->name, status);
|
||||
goto out_fail;
|
||||
}
|
||||
|
||||
for (i = 0, d = &p->stpg_descriptors[0]; i < p->stpg_descriptors_cnt; i++, d++) {
|
||||
struct scst_target_group *tg = __lookup_tg_by_group_id(dg, d->group_id);
|
||||
|
||||
if (!tg) {
|
||||
PRINT_ERROR("STPG: unable to find TG %d", d->group_id);
|
||||
goto out_fail;
|
||||
} else if (tg->state == scst_alua_name_to_state(d->prev_state)) {
|
||||
PRINT_ERROR("on_stpg script did not change ALUA state"
|
||||
" for device group %s / target group %s",
|
||||
dg->name, tg->name);
|
||||
goto out_fail;
|
||||
}
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&scst_dg_mutex);
|
||||
mutex_unlock(&scst_mutex);
|
||||
|
||||
scst_stpg_del_unblock_next(cmd);
|
||||
|
||||
cmd->completed = 1;
|
||||
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_THREAD);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
|
||||
out_fail:
|
||||
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_set_target_pgs_failed));
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the ALUA filter of those LUNs (tgt_dev) whose target port is a member
|
||||
* of target group @tg and that export a device that is a member of the device
|
||||
* group @tg->dg.
|
||||
*/
|
||||
static void __scst_tg_set_state(struct scst_target_group *tg,
|
||||
enum scst_tg_state state,
|
||||
struct scst_tgt *no_ua_tgt)
|
||||
enum scst_tg_state state)
|
||||
{
|
||||
struct scst_dg_dev *dg_dev;
|
||||
struct scst_device *dev;
|
||||
struct scst_tgt_dev *tgt_dev;
|
||||
struct scst_tg_tgt *tg_tgt;
|
||||
struct scst_tgt *tgt;
|
||||
enum scst_tg_state old_state = tg->state;
|
||||
|
||||
sBUG_ON(state >= ARRAY_SIZE(scst_alua_filter));
|
||||
lockdep_assert_held(&scst_dg_mutex);
|
||||
@@ -749,17 +960,26 @@ static void __scst_tg_set_state(struct scst_target_group *tg,
|
||||
|
||||
list_for_each_entry(dg_dev, &tg->dg->dev_list, entry) {
|
||||
dev = dg_dev->dev;
|
||||
if (dev->handler->on_alua_state_change_start != NULL)
|
||||
dev->handler->on_alua_state_change_start(dev, old_state, state);
|
||||
list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list,
|
||||
dev_tgt_dev_list_entry) {
|
||||
tgt = tgt_dev->sess->tgt;
|
||||
list_for_each_entry(tg_tgt, &tg->tgt_list, entry) {
|
||||
if (tg_tgt->tgt == tgt) {
|
||||
bool gen_ua = (state != SCST_TG_STATE_TRANSITIONING);
|
||||
|
||||
if ((tg->dg->stpg_rel_tgt_id == tgt_dev->sess->tgt->rel_tgt_id) &&
|
||||
tid_equal(tg->dg->stpg_transport_id, tgt_dev->sess->transport_id))
|
||||
gen_ua = false;
|
||||
scst_tg_change_tgt_dev_state(tgt_dev,
|
||||
state, tgt != no_ua_tgt);
|
||||
state, gen_ua);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dev->handler->on_alua_state_change_finish != NULL)
|
||||
dev->handler->on_alua_state_change_finish(dev, old_state, state);
|
||||
}
|
||||
|
||||
scst_check_alua_invariant();
|
||||
@@ -780,7 +1000,7 @@ int scst_tg_set_state(struct scst_target_group *tg, enum scst_tg_state state)
|
||||
if (res)
|
||||
goto out;
|
||||
|
||||
__scst_tg_set_state(tg, state, NULL);
|
||||
__scst_tg_set_state(tg, state);
|
||||
|
||||
mutex_unlock(&scst_dg_mutex);
|
||||
out:
|
||||
@@ -945,9 +1165,16 @@ out_free:
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* scst_dg_mutex supposed to be locked */
|
||||
static void __scst_dg_dev_remove(struct scst_dev_group *dg,
|
||||
struct scst_dg_dev *dgdev)
|
||||
{
|
||||
if (dgdev->dev->stpg_ext_blocked) {
|
||||
TRACE_DBG("DG %s remove: unblocking STPG ext blocked "
|
||||
"dev %s", dg->name, dgdev->dev->virt_name);
|
||||
scst_ext_unblock_dev(dgdev->dev, true);
|
||||
dgdev->dev->stpg_ext_blocked = 0;
|
||||
}
|
||||
list_del(&dgdev->entry);
|
||||
scst_dg_dev_sysfs_del(dg, dgdev);
|
||||
scst_reset_dev_alua_filter(dgdev->dev);
|
||||
@@ -1080,7 +1307,7 @@ static void __scst_dg_remove(struct scst_dev_group *dg)
|
||||
list_del(&dg->entry);
|
||||
scst_dg_sysfs_del(dg);
|
||||
list_for_each_entry(tg, &dg->tg_list, entry)
|
||||
__scst_tg_set_state(tg, SCST_TG_STATE_OPTIMIZED, NULL);
|
||||
__scst_tg_set_state(tg, SCST_TG_STATE_OPTIMIZED);
|
||||
while (!list_empty(&dg->dev_list)) {
|
||||
dgdev = list_first_entry(&dg->dev_list, struct scst_dg_dev,
|
||||
entry);
|
||||
@@ -1201,10 +1428,10 @@ out_unlock:
|
||||
EXPORT_SYMBOL_GPL(scst_lookup_tg_id);
|
||||
|
||||
/**
|
||||
* scst_impl_alua_configured() - Whether implicit ALUA has been configured.
|
||||
* scst_alua_configured() - Whether implicit ALUA has been configured.
|
||||
* @dev: Pointer to the SCST device to verify.
|
||||
*/
|
||||
bool scst_impl_alua_configured(struct scst_device *dev)
|
||||
bool scst_alua_configured(struct scst_device *dev)
|
||||
{
|
||||
struct scst_dev_group *dg;
|
||||
|
||||
@@ -1214,7 +1441,7 @@ bool scst_impl_alua_configured(struct scst_device *dev)
|
||||
|
||||
return dg != NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scst_impl_alua_configured);
|
||||
EXPORT_SYMBOL_GPL(scst_alua_configured);
|
||||
|
||||
/**
|
||||
* scst_tg_get_group_info() - Build REPORT TARGET GROUPS response.
|
||||
@@ -1329,3 +1556,362 @@ out:
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scst_tg_get_group_info);
|
||||
|
||||
struct scst_stpg_wait {
|
||||
atomic_t stpg_wait_left;
|
||||
int status;
|
||||
struct scst_dev_group *dg;
|
||||
struct scst_event_entry *event_entry;
|
||||
};
|
||||
|
||||
/* No locks */
|
||||
static void scst_stpg_check_blocking_done(struct scst_stpg_wait *wait)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_DBG("wait %p, left %d", wait, atomic_read(&wait->stpg_wait_left));
|
||||
|
||||
if (atomic_dec_and_test(&wait->stpg_wait_left)) {
|
||||
if (wait->status == 0)
|
||||
scst_event_queue(SCST_EVENT_STPG_USER_INVOKE,
|
||||
SCST_EVENT_SCST_CORE_ISSUER, wait->event_entry);
|
||||
else {
|
||||
wait->event_entry->event_notify_fn(&wait->event_entry->event,
|
||||
wait->event_entry->notify_fn_priv, wait->status);
|
||||
}
|
||||
kfree(wait);
|
||||
}
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
/* No locks */
|
||||
static void scst_stpg_ext_blocking_done(struct scst_device *dev,
|
||||
uint8_t *data, int len)
|
||||
{
|
||||
sBUG_ON(len != sizeof(data));
|
||||
scst_stpg_check_blocking_done(*((struct scst_stpg_wait **)data));
|
||||
}
|
||||
|
||||
/**
|
||||
* scst_tg_set_group_info - SET TARGET PORT GROUPS implementation.
|
||||
*
|
||||
* Returns >=0 upon success or negative error code otherwise, for instance,
|
||||
* if either an invalid group ID has been specified or the group ID
|
||||
* of a target group with one, or more non-local target ports has been
|
||||
* specified. In the error case the cmd has its sense set.
|
||||
*
|
||||
* In case of returned 0 the command completed asynchronously, i.e. upon
|
||||
* return might be already dead!!
|
||||
*/
|
||||
int scst_tg_set_group_info(struct scst_cmd *cmd)
|
||||
{
|
||||
struct scst_device *dev = cmd->dev;
|
||||
uint8_t *buf;
|
||||
int len;
|
||||
int i, j, res = 1, tpg_desc_count, valid_desc_count;
|
||||
struct scst_dev_group *dg;
|
||||
struct osi {
|
||||
uint16_t group_id;
|
||||
struct scst_target_group *tg;
|
||||
enum scst_tg_state prev_state;
|
||||
enum scst_tg_state new_state;
|
||||
} *osi = NULL;
|
||||
int event_entry_len, payload_len;
|
||||
struct scst_event_entry *event_entry;
|
||||
struct scst_event *event;
|
||||
struct scst_event_stpg_payload *payload;
|
||||
struct scst_event_stpg_descr *descr;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
len = scst_get_buf_full(cmd, &buf);
|
||||
if (len < 0) {
|
||||
PRINT_ERROR("scst_get_buf_full() failed: %d", len);
|
||||
res = len;
|
||||
if (len == -ENOMEM)
|
||||
scst_set_busy(cmd);
|
||||
else
|
||||
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* From SPC-4: "A parameter list length of zero specifies that no data
|
||||
* shall be transferred, and that no change shall be made in the
|
||||
* target port asymmetric access state of any target port groups or
|
||||
* target ports".
|
||||
*/
|
||||
if (len == 0)
|
||||
goto out_put;
|
||||
|
||||
tpg_desc_count = (len - 4) / 4;
|
||||
/* Check for some reasonable limit */
|
||||
if (tpg_desc_count > 64) {
|
||||
PRINT_ERROR("Too many STPG descriptors (%d) for dev %s",
|
||||
tpg_desc_count, dev->virt_name);
|
||||
res = -EINVAL;
|
||||
scst_set_invalid_field_in_cdb(cmd, 6, 0);
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
TRACE_DBG("tpg_desc_count %d", tpg_desc_count);
|
||||
|
||||
osi = kcalloc(tpg_desc_count, sizeof(*osi), GFP_KERNEL);
|
||||
if (!osi) {
|
||||
res = -ENOMEM;
|
||||
scst_set_busy(cmd);
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
res = mutex_lock_interruptible(&scst_mutex);
|
||||
if (res) {
|
||||
PRINT_INFO("mutex_lock_interruptible() returned %d, finishing "
|
||||
"cmd %p", res, cmd);
|
||||
scst_set_busy(cmd);
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
res = mutex_lock_interruptible(&scst_dg_mutex);
|
||||
if (res) {
|
||||
PRINT_INFO("mutex_lock_interruptible() returned %d, finishing "
|
||||
"cmd %p", res, cmd);
|
||||
scst_set_busy(cmd);
|
||||
goto out_unlock_sm_fail;
|
||||
}
|
||||
|
||||
dg = __lookup_dg_by_dev(dev);
|
||||
if (!dg) {
|
||||
res = -EINVAL;
|
||||
goto out_unlock_fail;
|
||||
}
|
||||
|
||||
TRACE_DBG("dg %s (%p) found, dev %s", dg->name, dg, dev->virt_name);
|
||||
|
||||
for (i = 4, j = 0; i + 4 <= len; i += 4, j++) {
|
||||
#ifndef __CHECKER__
|
||||
/*
|
||||
* Hide the statement below for smatch because otherwise it
|
||||
* triggers a false positive.
|
||||
*/
|
||||
WARN_ON_ONCE(j >= tpg_desc_count);
|
||||
#endif
|
||||
osi[j].new_state = buf[i] & 0x1f;
|
||||
switch (osi[j].new_state) {
|
||||
case SCST_TG_STATE_OPTIMIZED:
|
||||
case SCST_TG_STATE_NONOPTIMIZED:
|
||||
case SCST_TG_STATE_STANDBY:
|
||||
case SCST_TG_STATE_UNAVAILABLE:
|
||||
case SCST_TG_STATE_OFFLINE:
|
||||
break;
|
||||
default:
|
||||
TRACE_MGMT_DBG("Incorrect new state %d", osi[j].new_state);
|
||||
res = -EINVAL;
|
||||
goto out_unlock_fail;
|
||||
}
|
||||
|
||||
osi[j].group_id = get_unaligned_be16(&buf[i + 2]);
|
||||
if (!osi[j].group_id) {
|
||||
TRACE_MGMT_DBG("Invalid group_id %d", osi[j].group_id);
|
||||
res = -EINVAL;
|
||||
goto out_unlock_fail;
|
||||
}
|
||||
|
||||
osi[j].tg = __lookup_tg_by_group_id(dg, osi[j].group_id);
|
||||
if (!osi[j].tg) {
|
||||
TRACE_MGMT_DBG("No TG for group_id %d", osi[j].group_id);
|
||||
res = -ESRCH;
|
||||
goto out_unlock_fail;
|
||||
}
|
||||
|
||||
if (osi[j].tg->state == SCST_TG_STATE_TRANSITIONING) {
|
||||
TRACE_MGMT_DBG("TG %p is transitioning", osi[j].tg);
|
||||
res = -EBUSY;
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_alua_transitioning));
|
||||
/* second sense will not override the set one */
|
||||
goto out_unlock_fail;
|
||||
}
|
||||
osi[j].prev_state = osi[j].tg->state;
|
||||
|
||||
TRACE_DBG("j %d, group_id %u, tg %s (%p), state %d", j, osi[j].group_id,
|
||||
osi[j].tg->name, osi[j].tg, osi[j].tg->state);
|
||||
}
|
||||
|
||||
mutex_unlock(&scst_dg_mutex);
|
||||
mutex_unlock(&scst_mutex);
|
||||
|
||||
scst_put_buf_full(cmd, buf);
|
||||
|
||||
payload_len = sizeof(*payload) + sizeof(*descr) * tpg_desc_count;
|
||||
event_entry_len = sizeof(*event_entry) + payload_len;
|
||||
event_entry = kzalloc(event_entry_len, GFP_KERNEL);
|
||||
if (event_entry == NULL) {
|
||||
PRINT_ERROR("Unable to allocate event (size %d)", event_entry_len);
|
||||
res = -ENOMEM;
|
||||
scst_set_busy(cmd);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
TRACE_MEM("event_entry %p (len %d) allocated", event_entry,
|
||||
event_entry_len);
|
||||
|
||||
event = &event_entry->event;
|
||||
event->payload_len = payload_len;
|
||||
|
||||
payload = (struct scst_event_stpg_payload *)event->payload;
|
||||
payload->stpg_cmd_tag = cmd->tag;
|
||||
|
||||
res = 1;
|
||||
|
||||
if (strlen(dev->virt_name) >= sizeof(payload->device_name)) {
|
||||
PRINT_ERROR("Device name %s too long", dev->virt_name);
|
||||
goto out_too_long;
|
||||
}
|
||||
strlcpy(payload->device_name, dev->virt_name, sizeof(payload->device_name));
|
||||
|
||||
valid_desc_count = 0;
|
||||
for (j = 0, descr = &payload->stpg_descriptors[0]; j < tpg_desc_count; j++) {
|
||||
if (osi[j].prev_state == osi[j].new_state)
|
||||
continue;
|
||||
|
||||
if (strlen(scst_alua_state_name(osi[j].prev_state)) >= sizeof(descr->prev_state)) {
|
||||
PRINT_ERROR("prev state too long (%d)", osi[j].prev_state);
|
||||
goto out_too_long;
|
||||
}
|
||||
strlcpy(descr->prev_state, scst_alua_state_name(osi[j].prev_state),
|
||||
sizeof(descr->prev_state));
|
||||
|
||||
if (strlen(scst_alua_state_name(osi[j].new_state)) >= sizeof(descr->new_state)) {
|
||||
PRINT_ERROR("new state too long (%d)", osi[j].new_state);
|
||||
goto out_too_long;
|
||||
}
|
||||
strlcpy(descr->new_state, scst_alua_state_name(osi[j].new_state),
|
||||
sizeof(descr->new_state));
|
||||
|
||||
if (strlen(dg->name) >= sizeof(descr->dg_name)) {
|
||||
PRINT_ERROR("dg_name too long (%s)", dg->name);
|
||||
goto out_too_long;
|
||||
}
|
||||
strlcpy(descr->dg_name, dg->name, sizeof(descr->dg_name));
|
||||
|
||||
if (strlen(osi[j].tg->name) >= sizeof(descr->tg_name)) {
|
||||
PRINT_ERROR("tg_name too long (%s)", osi[j].tg->name);
|
||||
goto out_too_long;
|
||||
}
|
||||
strlcpy(descr->tg_name, osi[j].tg->name,
|
||||
sizeof(descr->tg_name));
|
||||
|
||||
descr->group_id = osi[j].group_id;
|
||||
|
||||
TRACE_DBG("group_id %u, prev_state %s, new_state %s, dg_name %s, "
|
||||
"tg_name %s", descr->group_id, descr->prev_state,
|
||||
descr->new_state, descr->dg_name, descr->tg_name);
|
||||
|
||||
valid_desc_count++;
|
||||
descr++;
|
||||
}
|
||||
|
||||
payload->stpg_descriptors_cnt = valid_desc_count;
|
||||
|
||||
if (valid_desc_count > 0) {
|
||||
struct scst_dg_dev *dgd;
|
||||
struct scst_stpg_wait *wait;
|
||||
int rc;
|
||||
|
||||
dg->stpg_rel_tgt_id = cmd->tgt->rel_tgt_id;
|
||||
dg->stpg_transport_id = kmemdup(cmd->sess->transport_id,
|
||||
scst_tid_size(cmd->sess->transport_id), GFP_KERNEL);
|
||||
if (dg->stpg_transport_id == NULL) {
|
||||
PRINT_ERROR("Unable to duplicate stpg_transport_id");
|
||||
goto out_free_event;
|
||||
}
|
||||
|
||||
wait = kzalloc(sizeof(*wait), GFP_KERNEL);
|
||||
if (wait == NULL) {
|
||||
PRINT_ERROR("Unable to allocate STPG wait struct "
|
||||
"(size %zd)", sizeof(*wait));
|
||||
scst_set_busy(cmd);
|
||||
res = -ENOMEM;
|
||||
goto out_free_tr_id;
|
||||
}
|
||||
|
||||
atomic_set(&wait->stpg_wait_left, 1);
|
||||
wait->event_entry = event_entry;
|
||||
|
||||
event_entry->event_notify_fn = scst_event_stpg_notify_fn;
|
||||
event_entry->notify_fn_priv = cmd;
|
||||
|
||||
mutex_lock(&scst_dg_mutex);
|
||||
list_for_each_entry(dgd, &dg->dev_list, entry) {
|
||||
if (dgd->dev == dev)
|
||||
continue;
|
||||
|
||||
TRACE_DBG("STPG: ext blocking dev %s", dgd->dev->virt_name);
|
||||
|
||||
atomic_inc(&wait->stpg_wait_left);
|
||||
|
||||
spin_lock_bh(&dev->dev_lock);
|
||||
WARN_ON(dgd->dev->stpg_ext_blocked);
|
||||
dgd->dev->stpg_ext_blocked = 1;
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
|
||||
rc = scst_ext_block_dev(dgd->dev, false,
|
||||
scst_stpg_ext_blocking_done, (uint8_t *)&wait,
|
||||
sizeof(wait));
|
||||
if (rc != 0) {
|
||||
TRACE_DBG("scst_ext_block_dev() returned %d, "
|
||||
"stepping back (cmd %p)", rc, cmd);
|
||||
wait->status = rc;
|
||||
wait->dg = dg;
|
||||
atomic_dec(&wait->stpg_wait_left);
|
||||
spin_lock_bh(&dev->dev_lock);
|
||||
WARN_ON(dgd->dev->stpg_ext_blocked);
|
||||
dgd->dev->stpg_ext_blocked = 0;
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&scst_dg_mutex);
|
||||
|
||||
scst_stpg_check_blocking_done(wait);
|
||||
/* !! cmd can be already dead here !! */
|
||||
} else {
|
||||
TRACE_DBG("Nothing to do");
|
||||
goto out_free_event;
|
||||
}
|
||||
|
||||
res = 0;
|
||||
|
||||
out_free:
|
||||
kfree(osi);
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
out_unlock_fail:
|
||||
mutex_unlock(&scst_dg_mutex);
|
||||
|
||||
out_unlock_sm_fail:
|
||||
mutex_unlock(&scst_mutex);
|
||||
|
||||
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_set_target_pgs_failed));
|
||||
|
||||
out_put:
|
||||
scst_put_buf_full(cmd, buf);
|
||||
goto out_free;
|
||||
|
||||
out_too_long:
|
||||
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_set_target_pgs_failed));
|
||||
res = -EOVERFLOW;
|
||||
|
||||
out_free_tr_id:
|
||||
kfree(dg->stpg_transport_id);
|
||||
|
||||
out_free_event:
|
||||
kfree(event_entry);
|
||||
goto out_free;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scst_tg_set_group_info);
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
obj-$(CONFIG_SCST_LOCAL) += scst_local.o
|
||||
|
||||
+6
-14
@@ -88,7 +88,7 @@ static unsigned long scst_local_trace_flag = SCST_LOCAL_DEFAULT_LOG_FLAGS;
|
||||
#define scsi_bufflen(cmd) ((cmd)->request_bufflen)
|
||||
#endif
|
||||
|
||||
#define SCST_LOCAL_VERSION "3.1"
|
||||
#define SCST_LOCAL_VERSION "3.2"
|
||||
static const char *scst_local_version_date = "20110901";
|
||||
|
||||
/* Some statistics */
|
||||
@@ -446,11 +446,7 @@ static ssize_t scst_local_scsi_transport_version_store(struct kobject *kobj,
|
||||
if (!tgt)
|
||||
goto out_up;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
|
||||
res = kstrtoul(buffer, 0, &val);
|
||||
#else
|
||||
res = strict_strtoul(buffer, 0, &val);
|
||||
#endif
|
||||
if (res != 0) {
|
||||
PRINT_ERROR("strtoul() for %s failed: %zd", buffer, res);
|
||||
goto out_up;
|
||||
@@ -516,11 +512,7 @@ static ssize_t scst_local_phys_transport_version_store(struct kobject *kobj,
|
||||
if (!tgt)
|
||||
goto out_up;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
|
||||
res = kstrtoul(buffer, 0, &val);
|
||||
#else
|
||||
res = strict_strtoul(buffer, 0, &val);
|
||||
#endif
|
||||
if (res != 0) {
|
||||
PRINT_ERROR("strtoul() for %s failed: %zd", buffer, res);
|
||||
goto out_up;
|
||||
@@ -778,6 +770,7 @@ static ssize_t scst_local_sysfs_mgmt_cmd(char *buf)
|
||||
res = __scst_local_add_adapter(tgt, session_name, true);
|
||||
} else if (strcasecmp("del_session", command) == 0) {
|
||||
struct scst_local_sess *s, *sess = NULL;
|
||||
|
||||
list_for_each_entry(s, &tgt->sessions_list,
|
||||
sessions_list_entry) {
|
||||
if (strcmp(s->scst_sess->initiator_name, session_name) == 0) {
|
||||
@@ -897,8 +890,7 @@ static void scst_local_copy_sense(struct scsi_cmnd *cmnd, struct scst_cmd *scst_
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
scst_cmnd_sense_len = (SCSI_SENSE_BUFFERSIZE < scst_cmnd_sense_len ?
|
||||
SCSI_SENSE_BUFFERSIZE : scst_cmnd_sense_len);
|
||||
scst_cmnd_sense_len = min(scst_cmnd_sense_len, SCSI_SENSE_BUFFERSIZE);
|
||||
memcpy(cmnd->sense_buffer, scst_cmd_get_sense_buffer(scst_cmnd),
|
||||
scst_cmnd_sense_len);
|
||||
|
||||
@@ -1671,7 +1663,7 @@ static int scst_local_driver_probe(struct device *dev)
|
||||
TRACE_DBG("sess %p", sess);
|
||||
|
||||
hpnt = scsi_host_alloc(&scst_lcl_ini_driver_template, sizeof(*sess));
|
||||
if (NULL == hpnt) {
|
||||
if (hpnt == NULL) {
|
||||
PRINT_ERROR("%s", "scsi_register() failed");
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
@@ -1828,7 +1820,7 @@ static int __scst_local_add_adapter(struct scst_local_tgt *tgt,
|
||||
|
||||
/* It's read-mostly, so cache alignment isn't needed */
|
||||
sess = kzalloc(sizeof(*sess), GFP_KERNEL);
|
||||
if (NULL == sess) {
|
||||
if (sess == NULL) {
|
||||
PRINT_ERROR("Unable to alloc scst_lcl_host (size %zu)",
|
||||
sizeof(*sess));
|
||||
res = -ENOMEM;
|
||||
@@ -1947,7 +1939,7 @@ static int scst_local_add_target(const char *target_name,
|
||||
TRACE_ENTRY();
|
||||
|
||||
tgt = kzalloc(sizeof(*tgt), GFP_KERNEL);
|
||||
if (NULL == tgt) {
|
||||
if (tgt == NULL) {
|
||||
PRINT_ERROR("Unable to alloc tgt (size %zu)", sizeof(*tgt));
|
||||
res = -ENOMEM;
|
||||
goto out;
|
||||
|
||||
+3
-11
@@ -211,17 +211,7 @@ fe80:0000:0000:0000:0002:c903:0005:f350
|
||||
Session names
|
||||
-------------
|
||||
|
||||
The name assigned by the ib_srpt target driver to a session depends on the
|
||||
mode in which it is operating. If one_target_per_port=y then the source port
|
||||
GID is used as the session name. If one_target_per_port=n then the 128-bit SRP
|
||||
initiator port identifier is used as the session name. This identifier is sent
|
||||
by the SRP initiator to the SRP target via the SRP_LOGIN_REQ information unit.
|
||||
The Linux SRP initiator (ib_srp) generates the initiator port identifier as
|
||||
follows:
|
||||
- The first eight bytes are the identifier extension ('initiator_ext' parameter
|
||||
specified in the login string echoed into the sysfs file 'add_target').
|
||||
- The last eight bytes are the GUID of the initiator HCA port used to
|
||||
communicate with the target.
|
||||
The ib_srpt target driver uses the source port GID as session name.
|
||||
|
||||
An example:
|
||||
|
||||
@@ -242,7 +232,9 @@ fe80:0000:0000:0000:0002:c903:0003:cca8
|
||||
|
||||
$ (cd /sys/kernel/scst_tgt/targets/ib_srpt/[0-9a-f]* && ls -d sessions/*)
|
||||
sessions/fe80:0000:0000:0000:0002:c903:0003:cca7
|
||||
sessions/fe80:0000:0000:0000:0002:c903:0003:cca8
|
||||
sessions/fe80:0000:0000:0000:0002:c903:0005:f34b
|
||||
sessions/fe80:0000:0000:0000:0002:c903:0005:f34c
|
||||
|
||||
|
||||
LUN masking
|
||||
|
||||
+5
-15
@@ -80,7 +80,7 @@
|
||||
|
||||
/* Name of this kernel module. */
|
||||
#define DRV_NAME "ib_srpt"
|
||||
#define DRV_VERSION "3.1.0-pre#" __stringify(OFED_FLAVOR)
|
||||
#define DRV_VERSION "3.2.0-pre#" __stringify(OFED_FLAVOR)
|
||||
#define DRV_RELDATE "(not yet released)"
|
||||
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
|
||||
/* Flags to be used in SCST debug tracing statements. */
|
||||
@@ -169,16 +169,6 @@ module_param(use_node_guid_in_target_name, bool, 0444);
|
||||
MODULE_PARM_DESC(use_node_guid_in_target_name,
|
||||
"Use HCA node GUID as SCST target name.");
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) \
|
||||
|| defined(RHEL_MAJOR) && RHEL_MAJOR -0 <= 5
|
||||
static int one_target_per_port = true;
|
||||
#else
|
||||
static bool one_target_per_port = true;
|
||||
#endif
|
||||
module_param(one_target_per_port, bool, 0444);
|
||||
MODULE_PARM_DESC(one_target_per_port,
|
||||
"One SCST target per HCA port instead of one per HCA.");
|
||||
|
||||
static struct rdma_cm_id *rdma_cm_id;
|
||||
|
||||
static int srpt_get_u64_x(char *buffer, struct kernel_param *kp)
|
||||
@@ -862,7 +852,7 @@ static struct srpt_ioctx **srpt_alloc_ioctx_ring(struct srpt_device *sdev,
|
||||
WARN_ON(ioctx_size != sizeof(struct srpt_recv_ioctx) &&
|
||||
ioctx_size != sizeof(struct srpt_send_ioctx));
|
||||
|
||||
ring = kmalloc(ring_size * sizeof(ring[0]), GFP_KERNEL);
|
||||
ring = kmalloc_array(ring_size, sizeof(ring[0]), GFP_KERNEL);
|
||||
if (!ring)
|
||||
goto out;
|
||||
for (i = 0; i < ring_size; ++i) {
|
||||
@@ -1164,8 +1154,8 @@ static int srpt_get_desc_tbl(struct srpt_recv_ioctx *recv_ioctx,
|
||||
if (ioctx->n_rbuf == 1)
|
||||
ioctx->rbufs = &ioctx->single_rbuf;
|
||||
else {
|
||||
ioctx->rbufs =
|
||||
kmalloc(ioctx->n_rbuf * sizeof(*db), GFP_ATOMIC);
|
||||
ioctx->rbufs = kmalloc_array(ioctx->n_rbuf,
|
||||
sizeof(*db), GFP_ATOMIC);
|
||||
if (!ioctx->rbufs) {
|
||||
ioctx->n_rbuf = 0;
|
||||
ret = -ENOMEM;
|
||||
@@ -1650,7 +1640,7 @@ static int srpt_build_cmd_rsp(struct srpt_rdma_ch *ch,
|
||||
if (!scst_sense_valid(sense_data)) {
|
||||
sense_data_len = 0;
|
||||
} else {
|
||||
BUILD_BUG_ON(MIN_MAX_RSP_SIZE <= sizeof(*srp_rsp));
|
||||
BUILD_BUG_ON(sizeof(*srp_rsp) >= MIN_MAX_RSP_SIZE);
|
||||
max_sense_len = ch->max_ti_iu_len - sizeof(*srp_rsp);
|
||||
if (sense_data_len > max_sense_len) {
|
||||
pr_warn("truncated sense data from %d to %d bytes\n",
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
#
|
||||
# Common makefile for SCSI target mid-level and its drivers
|
||||
#
|
||||
# Copyright (C) 2004 - 2015 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
# Copyright (C) 2004 - 2005 Leonid Stoljar
|
||||
# Copyright (C) 2007 - 2015 SanDisk Corporation
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation, version 2
|
||||
# of the License.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
#
|
||||
|
||||
SHELL=/bin/bash
|
||||
# Decide to use which kernel src. If not specified, is current running kernel.
|
||||
#export KDIR=/usr/src/linux-2.6
|
||||
|
||||
FILEIO_DIR=fileio
|
||||
STPGD_DIR=stpgd
|
||||
EVENTS_DIR=events
|
||||
|
||||
all:
|
||||
cd $(FILEIO_DIR) && $(MAKE) $@
|
||||
cd $(STPGD_DIR) && $(MAKE) $@
|
||||
# cd $(EVENTS_DIR) && $(MAKE) $@
|
||||
|
||||
install:
|
||||
cd $(FILEIO_DIR) && $(MAKE) $@
|
||||
cd $(STPGD_DIR) && $(MAKE) $@
|
||||
# cd $(EVENTS_DIR) && $(MAKE) $@
|
||||
|
||||
uninstall:
|
||||
cd $(FILEIO_DIR) && $(MAKE) $@
|
||||
cd $(STPGD_DIR) && $(MAKE) $@
|
||||
cd $(EVENTS_DIR) && $(MAKE) $@
|
||||
|
||||
clean:
|
||||
cd $(FILEIO_DIR) && $(MAKE) $@
|
||||
cd $(STPGD_DIR) && $(MAKE) $@
|
||||
cd $(EVENTS_DIR) && $(MAKE) $@
|
||||
|
||||
extraclean:
|
||||
cd $(FILEIO_DIR) && $(MAKE) $@
|
||||
cd $(STPGD_DIR) && $(MAKE) $@
|
||||
cd $(EVENTS_DIR) && $(MAKE) $@
|
||||
|
||||
2release:
|
||||
cd $(FILEIO_DIR) && $(MAKE) $@
|
||||
cd $(STPGD_DIR) && $(MAKE) $@
|
||||
cd $(EVENTS_DIR) && $(MAKE) $@
|
||||
|
||||
2debug:
|
||||
cd $(FILEIO_DIR) && $(MAKE) $@
|
||||
cd $(STPGD_DIR) && $(MAKE) $@
|
||||
cd $(EVENTS_DIR) && $(MAKE) $@
|
||||
|
||||
2perf:
|
||||
cd $(FILEIO_DIR) && $(MAKE) $@
|
||||
cd $(STPGD_DIR) && $(MAKE) $@
|
||||
cd $(EVENTS_DIR) && $(MAKE) $@
|
||||
|
||||
disable_proc:
|
||||
cd $(FILEIO_DIR) && $(MAKE) $@
|
||||
cd $(STPGD_DIR) && $(MAKE) $@
|
||||
cd $(EVENTS_DIR) && $(MAKE) $@
|
||||
|
||||
enable_proc:
|
||||
cd $(FILEIO_DIR) && $(MAKE) $@
|
||||
cd $(STPGD_DIR) && $(MAKE) $@
|
||||
cd $(EVENTS_DIR) && $(MAKE) $@
|
||||
|
||||
help:
|
||||
@echo " all (the default) : make all"
|
||||
@echo " clean : clean files"
|
||||
@echo " extraclean : clean + clean dependencies"
|
||||
@echo " install : install"
|
||||
@echo " uninstall : uninstall"
|
||||
@echo " Notes :"
|
||||
@echo " - install and uninstall must be made as root."
|
||||
@echo " - be sure to compile qla against the correct initiator"
|
||||
@echo " driver. Read its README for details."
|
||||
|
||||
.PHONY: all install uninstall clean extraclean help 2release 2debug 2perf disable_proc enable_proc
|
||||
@@ -0,0 +1,102 @@
|
||||
ifndef PREFIX
|
||||
PREFIX=/usr/local
|
||||
endif
|
||||
|
||||
SHELL=/bin/bash
|
||||
|
||||
SRCS_F = events.c debug.c
|
||||
OBJS_F = $(SRCS_F:.c=.o)
|
||||
|
||||
#SRCS_C =
|
||||
#OBJS_C = $(SRCS_C:.c=.o)
|
||||
|
||||
SCST_INC_DIR := $(shell if [ -e "$$PWD/../../scst" ]; \
|
||||
then echo "$$PWD/../../scst/include"; \
|
||||
else echo "$(DESTDIR)$(PREFIX)/include/scst"; fi)
|
||||
DEBUG_INC_DIR := ../include
|
||||
INSTALL_DIR := $(DESTDIR)$(PREFIX)/bin/scst
|
||||
|
||||
CFLAGS += -O2 -Wall -Wextra -Wno-unused-parameter -Wstrict-prototypes \
|
||||
-I$(SCST_INC_DIR) -I$(DEBUG_INC_DIR) -D_GNU_SOURCE -D__USE_FILE_OFFSET64 \
|
||||
-D__USE_LARGEFILE64
|
||||
PROGS = events
|
||||
LIBS = -lpthread
|
||||
|
||||
CFLAGS += -DEXTRACHECKS
|
||||
#CFLAGS += -DTRACING
|
||||
CFLAGS += -DDEBUG -g -fno-inline -fno-inline-functions
|
||||
CFLAGS += -W -Wno-unused-parameter
|
||||
CFLAGS += $(LOCAL_CFLAGS)
|
||||
|
||||
#CFLAGS += -DDEBUG_NOMEM
|
||||
|
||||
all: $(PROGS)
|
||||
|
||||
events: .depend_f $(OBJS_F)
|
||||
$(CC) $(OBJS_F) $(LIBS) $(LOCAL_LD_FLAGS) -o $@
|
||||
|
||||
#cdrom_tgt: .depend_c $(OBJS_C)
|
||||
# $(CC) $(OBJS_C) $(LIBS) $(LOCAL_LD_FLAGS) -o $@
|
||||
|
||||
ifeq (.depend_f,$(wildcard .depend_f))
|
||||
-include .depend_f
|
||||
endif
|
||||
|
||||
#ifeq (.depend_c,$(wildcard .depend_c))
|
||||
#-include .depend_c
|
||||
#endif
|
||||
|
||||
%.o: %.c Makefile
|
||||
$(CC) -c -o $(@) $(CFLAGS) $(<)
|
||||
|
||||
.depend_f:
|
||||
$(CC) -M $(CFLAGS) $(SRCS_F) >$(@)
|
||||
|
||||
#.depend_c:
|
||||
# $(CC) -M $(CFLAGS) $(SRCS_C) >$(@)
|
||||
|
||||
install: all
|
||||
install -d $(INSTALL_DIR)
|
||||
install -m 755 $(PROGS) $(INSTALL_DIR)
|
||||
|
||||
uninstall:
|
||||
rm -f $(INSTALL_DIR)/$(PROGS)
|
||||
rm -rf $(INSTALL_DIR)
|
||||
|
||||
clean:
|
||||
rm -f *.o $(PROGS) .depend*
|
||||
|
||||
extraclean: clean
|
||||
rm -f *.orig *.rej
|
||||
|
||||
2release:
|
||||
sed -i.aa s/"^C\?FLAGS += \-DEXTRACHECKS"/"#CFLAGS += \-DEXTRACHECKS"/ Makefile
|
||||
grep "^#CFLAGS += \-DEXTRACHECKS" Makefile >/dev/null
|
||||
sed -i.aa s/"^#\?CFLAGS += \-DTRACING"/"CFLAGS += \-DTRACING"/ Makefile
|
||||
grep "^CFLAGS += \-DTRACING" Makefile >/dev/null
|
||||
sed -i.aa s/"^C\?FLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/"#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/ Makefile
|
||||
grep "^#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions" Makefile >/dev/null
|
||||
rm Makefile.aa
|
||||
|
||||
2debug:
|
||||
sed -i.aa s/"^#\?CFLAGS += \-DEXTRACHECKS"/"CFLAGS += \-DEXTRACHECKS"/ Makefile
|
||||
grep "^CFLAGS += \-DEXTRACHECKS" Makefile >/dev/null
|
||||
sed -i.aa s/"^C\?FLAGS += \-DTRACING"/"#CFLAGS += \-DTRACING"/ Makefile
|
||||
grep "^#CFLAGS += \-DTRACING" Makefile >/dev/null
|
||||
sed -i.aa s/"^#\?CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/"CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/ Makefile
|
||||
grep "^CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions" Makefile >/dev/null
|
||||
rm Makefile.aa
|
||||
|
||||
2perf:
|
||||
sed -i.aa s/"^C\?FLAGS += \-DEXTRACHECKS"/"#CFLAGS += \-DEXTRACHECKS"/ Makefile
|
||||
grep "^#CFLAGS += \-DEXTRACHECKS" Makefile >/dev/null
|
||||
sed -i.aa s/"^C\?FLAGS += \-DTRACING"/"#CFLAGS += \-DTRACING"/ Makefile
|
||||
grep "^#CFLAGS += \-DTRACING" Makefile >/dev/null
|
||||
sed -i.aa s/"^C\?FLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/"#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/ Makefile
|
||||
grep "^#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions" Makefile >/dev/null
|
||||
rm Makefile.aa
|
||||
|
||||
release-archive:
|
||||
../../scripts/generate-release-archive events "$$(sed -n 's/^#define[[:blank:]]VERSION_STR[[:blank:]]*\"\([^\"]*\)\".*/\1/p' events.c)"
|
||||
|
||||
.PHONY: all install uninstall clean extraclean 2release 2debug 2perf
|
||||
@@ -0,0 +1,2 @@
|
||||
This is test/demo application for SCST events subsystem. For more
|
||||
information about this subsystem, see scst_event.h file.
|
||||
@@ -0,0 +1 @@
|
||||
#include "../include/debug.c"
|
||||
@@ -0,0 +1,326 @@
|
||||
/*
|
||||
* events.c
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <getopt.h>
|
||||
#include <malloc.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/user.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "version.h"
|
||||
#include "debug.h"
|
||||
#include "scst_event.h"
|
||||
|
||||
char *app_name;
|
||||
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
|
||||
#ifdef DEBUG
|
||||
/*#define DEFAULT_LOG_FLAGS (TRACE_ALL & ~TRACE_MEMORY & ~TRACE_BUFF \
|
||||
& ~TRACE_FUNCTION)
|
||||
#define DEFAULT_LOG_FLAGS (TRACE_ALL & ~TRACE_MEMORY & ~TRACE_BUFF & \
|
||||
~TRACE_SCSI & ~TRACE_SCSI_SERIALIZING & ~TRACE_DEBUG)
|
||||
*/
|
||||
#define DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MINOR | TRACE_PID | \
|
||||
TRACE_FUNCTION | TRACE_SPECIAL | TRACE_MGMT | TRACE_MGMT_DEBUG | \
|
||||
TRACE_TIME)
|
||||
|
||||
#define TRACE_SN(args...) TRACE(TRACE_SCSI_SERIALIZING, args)
|
||||
|
||||
#else /* DEBUG */
|
||||
|
||||
# ifdef TRACING
|
||||
#define DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MGMT | \
|
||||
TRACE_TIME | TRACE_SPECIAL)
|
||||
# else
|
||||
#define DEFAULT_LOG_FLAGS 0
|
||||
# endif
|
||||
#endif /* DEBUG */
|
||||
|
||||
bool log_daemon = false; /* needed for the tracing infrastructure */
|
||||
unsigned long trace_flag = DEFAULT_LOG_FLAGS;
|
||||
#endif /* defined(DEBUG) || defined(TRACING) */
|
||||
|
||||
static struct option const long_options[] = {
|
||||
{"allowed_event", required_argument, 0, 'e'},
|
||||
{"allowed_issuer", required_argument, 0, 'i'},
|
||||
{"non_blocking", no_argument, 0, 'n'},
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
{"debug", required_argument, 0, 'd'},
|
||||
#endif
|
||||
{"version", no_argument, 0, 'v'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
#define MAX_ALLOWED_EVENTS 16
|
||||
static struct scst_event allowed_events[MAX_ALLOWED_EVENTS];
|
||||
static int allowed_events_num;
|
||||
static int non_blocking;
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
printf("Usage: %s [OPTIONS]\n", app_name);
|
||||
printf("\nSCST events testing program\n");
|
||||
printf(" -e, --allowed_event=code Allowed event code, 0 - any event\n");
|
||||
printf(" -i, --allowed_issuer=issuer Allowed issuer, * - any issuer\n");
|
||||
printf(" -n, --non_blocking Use non-blocking operations\n");
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
printf(" -d, --debug=level Debug tracing level\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void handle_tm_received(struct scst_event_user *event_user)
|
||||
{
|
||||
struct scst_event_tm_fn_received_payload *p = (struct scst_event_tm_fn_received_payload *)event_user->out_event.payload;
|
||||
|
||||
printf("fn %d, device %s\n", p->fn, p->device_name);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int res = 0, i;
|
||||
int ch, longindex;
|
||||
int event_fd;
|
||||
uint8_t event_user_buf[10240];
|
||||
struct scst_event_user *event_user = (struct scst_event_user *)event_user_buf;
|
||||
struct pollfd pl;
|
||||
|
||||
setlinebuf(stdout);
|
||||
|
||||
res = debug_init();
|
||||
if (res != 0)
|
||||
goto out;
|
||||
|
||||
app_name = argv[0];
|
||||
|
||||
while ((ch = getopt_long(argc, argv, "+e:i:d:nhv",
|
||||
long_options, &longindex)) >= 0) {
|
||||
switch (ch) {
|
||||
case 'e':
|
||||
if (allowed_events[allowed_events_num].event_code != 0) {
|
||||
allowed_events_num++;
|
||||
if (allowed_events_num >= MAX_ALLOWED_EVENTS) {
|
||||
PRINT_ERROR("Too many allowed events %d",
|
||||
allowed_events_num);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
allowed_events[allowed_events_num].event_code = atoi(optarg);
|
||||
break;
|
||||
case 'i':
|
||||
if (allowed_events[allowed_events_num].issuer_name[0] != '\0') {
|
||||
allowed_events_num++;
|
||||
if (allowed_events_num >= MAX_ALLOWED_EVENTS) {
|
||||
PRINT_ERROR("Too many allowed events %d",
|
||||
allowed_events_num);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
strncpy(allowed_events[allowed_events_num].issuer_name,
|
||||
optarg, sizeof(allowed_events[allowed_events_num].issuer_name));
|
||||
allowed_events[allowed_events_num].issuer_name[
|
||||
sizeof(allowed_events[allowed_events_num].issuer_name)-1] = '\0';
|
||||
break;
|
||||
case 'n':
|
||||
non_blocking = 1;
|
||||
break;
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
case 'd':
|
||||
trace_flag = strtol(optarg, (char **)NULL, 0);
|
||||
break;
|
||||
#endif
|
||||
case 'v':
|
||||
printf("%s version %s\n", app_name, VERSION_STR);
|
||||
goto out_done;
|
||||
default:
|
||||
goto out_usage;
|
||||
}
|
||||
}
|
||||
|
||||
if (allowed_events_num == 0) {
|
||||
if ((allowed_events[0].event_code == 0) &&
|
||||
(allowed_events[0].issuer_name[0] == '\0'))
|
||||
allowed_events[0].issuer_name[0] = '*';
|
||||
}
|
||||
|
||||
allowed_events_num++;
|
||||
|
||||
#ifdef DEBUG
|
||||
PRINT_INFO("trace_flag %lx", trace_flag);
|
||||
#endif
|
||||
|
||||
if (non_blocking)
|
||||
PRINT_INFO("%s", "NON-BLOCKING");
|
||||
|
||||
event_fd = open(SCST_EVENT_DEV, O_RDWR | (non_blocking ? O_NONBLOCK : 0));
|
||||
if (event_fd < 0) {
|
||||
res = -errno;
|
||||
PRINT_ERROR("Unable to open SCST event device %s (%s)",
|
||||
SCST_EVENT_DEV, strerror(-res));
|
||||
goto out_done;
|
||||
}
|
||||
|
||||
memset(&pl, 0, sizeof(pl));
|
||||
pl.fd = event_fd;
|
||||
pl.events = POLLIN;
|
||||
|
||||
for (i = 0; i < allowed_events_num; i++) {
|
||||
struct scst_event e;
|
||||
|
||||
PRINT_INFO("Setting allowed event code %d, issuer_name %s",
|
||||
allowed_events[i].event_code,
|
||||
allowed_events[i].issuer_name);
|
||||
memset(&e, 0, sizeof(e));
|
||||
e.event_code = allowed_events[i].event_code;
|
||||
strncpy(e.issuer_name, allowed_events[i].issuer_name,
|
||||
sizeof(e.issuer_name));
|
||||
e.issuer_name[sizeof(e.issuer_name)-1] = '\0';
|
||||
|
||||
res = ioctl(event_fd, SCST_EVENT_ALLOW_EVENT, &e);
|
||||
if (res != 0) {
|
||||
PRINT_ERROR("SCST_EVENT_ALLOW_EVENT failed: %s (res %d)",
|
||||
strerror(errno), res);
|
||||
goto out_done;
|
||||
}
|
||||
}
|
||||
|
||||
while (1) {
|
||||
memset(event_user_buf, 0, sizeof(event_user_buf));
|
||||
event_user->max_event_size = sizeof(event_user_buf);
|
||||
res = ioctl(event_fd, SCST_EVENT_GET_NEXT_EVENT, event_user);
|
||||
if (res != 0) {
|
||||
res = errno;
|
||||
switch (res) {
|
||||
case ESRCH:
|
||||
case EBUSY:
|
||||
TRACE_MGMT_DBG("SCST_EVENT_GET_NEXT_EVENT returned "
|
||||
"%d (%s)", res, strerror(res));
|
||||
/* go through */
|
||||
case EINTR:
|
||||
continue;
|
||||
case EAGAIN:
|
||||
TRACE_DBG("SCST_EVENT_GET_NEXT_EVENT, returned "
|
||||
"EAGAIN (%d)", res);
|
||||
if (non_blocking)
|
||||
break;
|
||||
else
|
||||
continue;
|
||||
default:
|
||||
PRINT_ERROR("SCST_EVENT_GET_NEXT_EVENT failed: %s (res %d)",
|
||||
strerror(errno), res);
|
||||
goto out_done;
|
||||
}
|
||||
again_poll:
|
||||
res = poll(&pl, 1, 2000);
|
||||
if (res > 0)
|
||||
continue;
|
||||
else if (res == 0)
|
||||
goto again_poll;
|
||||
else {
|
||||
res = errno;
|
||||
switch (res) {
|
||||
case ESRCH:
|
||||
case EBUSY:
|
||||
case EAGAIN:
|
||||
TRACE_MGMT_DBG("poll() returned %d "
|
||||
"(%s)", res, strerror(res));
|
||||
case EINTR:
|
||||
goto again_poll;
|
||||
default:
|
||||
PRINT_ERROR("poll() failed: %s", strerror(res));
|
||||
#if 1
|
||||
goto again_poll;
|
||||
#else
|
||||
goto out_close;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PRINT_INFO("\nevent_code %d, issuer_name %s",
|
||||
event_user->out_event.event_code,
|
||||
event_user->out_event.issuer_name);
|
||||
if (event_user->out_event.payload_len != 0)
|
||||
PRINT_BUFFER("payoad", event_user->out_event.payload,
|
||||
event_user->out_event.payload_len);
|
||||
PRINT_INFO("%s", "");
|
||||
|
||||
if (event_user->out_event.event_code == 0x12345) {
|
||||
struct scst_event_notify_done d;
|
||||
|
||||
PRINT_INFO("%s", "Press any key to send reply to event "
|
||||
"0x12345");
|
||||
getchar();
|
||||
|
||||
memset(&d, 0, sizeof(d));
|
||||
d.event_id = event_user->out_event.event_id;
|
||||
d.status = -19;
|
||||
res = ioctl(event_fd, SCST_EVENT_NOTIFY_DONE, &d);
|
||||
if (res != 0)
|
||||
PRINT_ERROR("SCST_EVENT_NOTIFY_DONE failed: %s "
|
||||
"(res %d)", strerror(errno), res);
|
||||
} else if (event_user->out_event.event_code == SCST_EVENT_TM_FN_RECEIVED)
|
||||
handle_tm_received(event_user);
|
||||
else if (event_user->out_event.event_code == SCST_EVENT_TM_FN_RECEIVED) {
|
||||
struct scst_event_notify_done d;
|
||||
|
||||
memset(&d, 0, sizeof(d));
|
||||
d.event_id = event_user->out_event.event_id;
|
||||
d.status = -20;
|
||||
res = ioctl(event_fd, SCST_EVENT_NOTIFY_DONE, &d);
|
||||
if (res != 0)
|
||||
PRINT_ERROR("SCST_EVENT_NOTIFY_DONE failed: %s "
|
||||
"(res %d)", strerror(errno), res);
|
||||
}
|
||||
|
||||
#if 0
|
||||
{
|
||||
struct scst_event e;
|
||||
|
||||
PRINT_INFO("Deleting allowed event code %d, issuer_name %s",
|
||||
allowed_events[0].event_code,
|
||||
allowed_events[0].issuer_name);
|
||||
memset(&e, 0, sizeof(e));
|
||||
e.event_code = allowed_events[0].event_code;
|
||||
strncpy(e.issuer_name, allowed_events[0].issuer_name,
|
||||
sizeof(e.issuer_name));
|
||||
e.issuer_name[sizeof(e.issuer_name)-1] = '\0';
|
||||
|
||||
res = ioctl(event_fd, SCST_EVENT_DISALLOW_EVENT, &e);
|
||||
if (res != 0) {
|
||||
PRINT_ERROR("SCST_EVENT_DISALLOW_EVENT failed: "
|
||||
"%s (res %d)", strerror(errno), res);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
out_done:
|
||||
debug_done();
|
||||
|
||||
out:
|
||||
return res;
|
||||
|
||||
out_usage:
|
||||
usage();
|
||||
goto out_done;
|
||||
}
|
||||
+3
-1
@@ -29,10 +29,11 @@ OBJS_F = $(SRCS_F:.c=.o)
|
||||
SCST_INC_DIR := $(shell if [ -e "$$PWD/../../scst" ]; \
|
||||
then echo "$$PWD/../../scst/include"; \
|
||||
else echo "$(DESTDIR)$(PREFIX)/include/scst"; fi)
|
||||
DEBUG_INC_DIR := ../include
|
||||
INSTALL_DIR := $(DESTDIR)$(PREFIX)/bin/scst
|
||||
|
||||
CFLAGS += -O2 -Wall -Wextra -Wno-unused-parameter -Wstrict-prototypes \
|
||||
-I$(SCST_INC_DIR) -D_GNU_SOURCE -D__USE_FILE_OFFSET64 \
|
||||
-I$(SCST_INC_DIR) -I$(DEBUG_INC_DIR) -D_GNU_SOURCE -D__USE_FILE_OFFSET64 \
|
||||
-D__USE_LARGEFILE64
|
||||
PROGS = fileio_tgt
|
||||
LIBS = -lpthread
|
||||
@@ -48,6 +49,7 @@ CFLAGS += $(LOCAL_CFLAGS)
|
||||
#CFLAGS += -DDEBUG_TM_IGNORE
|
||||
#CFLAGS += -DDEBUG_TM_IGNORE -DDEBUG_TM_FN_IGNORE
|
||||
#CFLAGS += -DDEBUG_TM_IGNORE_ALL
|
||||
CFLAGS += -DDEBUG_EXT_COPY_REMAP
|
||||
|
||||
all: $(PROGS)
|
||||
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
User space FILEIO handler
|
||||
=========================
|
||||
|
||||
Version 3.1.0, XX XXXXX 2014
|
||||
Version 3.2.0, XX XXXXX 2014
|
||||
----------------------------
|
||||
|
||||
User space program fileio_tgt uses interface of SCST's scst_user dev
|
||||
|
||||
+187
-22
@@ -47,6 +47,7 @@ static int exec_fsync(struct vdisk_cmd *vcmd);
|
||||
static void exec_read(struct vdisk_cmd *vcmd, loff_t loff);
|
||||
static void exec_write(struct vdisk_cmd *vcmd, loff_t loff);
|
||||
static void exec_verify(struct vdisk_cmd *vcmd, loff_t loff);
|
||||
static void exec_write_same(struct vdisk_cmd *vcmd);
|
||||
|
||||
static int open_dev_fd(struct vdisk_dev *dev)
|
||||
{
|
||||
@@ -68,11 +69,31 @@ static int open_dev_fd(struct vdisk_dev *dev)
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline void set_cmd_error_status(struct scst_user_scsi_cmd_reply_exec *reply,
|
||||
static void set_resp_data_len(struct vdisk_cmd *vcmd, int32_t resp_data_len)
|
||||
{
|
||||
struct scst_user_scsi_cmd_reply_exec *reply = &vcmd->reply->exec_reply;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (vcmd->may_need_to_free_pbuf && (resp_data_len == 0)) {
|
||||
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
|
||||
free((void *)(unsigned long)cmd->pbuf);
|
||||
cmd->pbuf = 0;
|
||||
reply->pbuf = 0;
|
||||
}
|
||||
|
||||
reply->resp_data_len = resp_data_len;
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
static inline void set_cmd_error_status(struct vdisk_cmd *vcmd,
|
||||
int status)
|
||||
{
|
||||
struct scst_user_scsi_cmd_reply_exec *reply = &vcmd->reply->exec_reply;
|
||||
reply->status = status;
|
||||
reply->resp_data_len = 0;
|
||||
set_resp_data_len(vcmd, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -107,7 +128,7 @@ void set_cmd_error(struct vdisk_cmd *vcmd, int key, int asc, int ascq)
|
||||
|
||||
EXTRACHECKS_BUG_ON(vcmd->cmd->subcode != SCST_USER_EXEC);
|
||||
|
||||
set_cmd_error_status(reply, SAM_STAT_CHECK_CONDITION);
|
||||
set_cmd_error_status(vcmd, SAM_STAT_CHECK_CONDITION);
|
||||
reply->sense_len = set_sense(vcmd->sense, sizeof(vcmd->sense), key,
|
||||
asc, ascq);
|
||||
reply->psense_buffer = (unsigned long)vcmd->sense;
|
||||
@@ -116,11 +137,11 @@ void set_cmd_error(struct vdisk_cmd *vcmd, int key, int asc, int ascq)
|
||||
return;
|
||||
}
|
||||
|
||||
void set_busy(struct scst_user_scsi_cmd_reply_exec *reply)
|
||||
void set_busy(struct vdisk_cmd *vcmd)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
set_cmd_error_status(reply, SAM_STAT_TASK_SET_FULL);
|
||||
set_cmd_error_status(vcmd, SAM_STAT_TASK_SET_FULL);
|
||||
TRACE_MGMT_DBG("%s", "Sending QUEUE_FULL status");
|
||||
|
||||
TRACE_EXIT();
|
||||
@@ -211,6 +232,9 @@ static int do_exec(struct vdisk_cmd *vcmd)
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
/* Must be reinitialized each time to avoid crash on stale value */
|
||||
vcmd->may_need_to_free_pbuf = 0;
|
||||
|
||||
switch(cmd->queue_type) {
|
||||
case SCST_CMD_QUEUE_ORDERED:
|
||||
TRACE(TRACE_ORDER, "ORDERED cmd_h %d", vcmd->cmd->cmd_h);
|
||||
@@ -250,6 +274,7 @@ static int do_exec(struct vdisk_cmd *vcmd)
|
||||
else
|
||||
#endif
|
||||
cmd->pbuf = (unsigned long)dev->alloc_fn(cmd->alloc_len);
|
||||
vcmd->may_need_to_free_pbuf = 1;
|
||||
TRACE_MEM("Buf %"PRIx64" alloced, len %d", cmd->pbuf,
|
||||
cmd->alloc_len);
|
||||
reply->pbuf = cmd->pbuf;
|
||||
@@ -257,7 +282,7 @@ static int do_exec(struct vdisk_cmd *vcmd)
|
||||
TRACE(TRACE_OUT_OF_MEM, "Unable to allocate buffer "
|
||||
"(len %d)", cmd->alloc_len);
|
||||
#ifndef DEBUG_NOMEM
|
||||
set_busy(reply);
|
||||
set_busy(vcmd);
|
||||
#endif
|
||||
goto out;
|
||||
}
|
||||
@@ -408,6 +433,10 @@ static int do_exec(struct vdisk_cmd *vcmd)
|
||||
case READ_CAPACITY:
|
||||
exec_read_capacity(vcmd);
|
||||
break;
|
||||
case WRITE_SAME_10:
|
||||
case WRITE_SAME_16:
|
||||
exec_write_same(vcmd);
|
||||
break;
|
||||
case SERVICE_ACTION_IN_16:
|
||||
if ((cmd->cdb[1] & 0x1f) == SAI_READ_CAPACITY_16)
|
||||
exec_read_capacity16(vcmd);
|
||||
@@ -519,6 +548,49 @@ static int do_on_free_cmd(struct vdisk_cmd *vcmd)
|
||||
return res;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_EXT_COPY_REMAP
|
||||
static int do_ext_copy_remap(struct vdisk_cmd *vcmd)
|
||||
{
|
||||
struct scst_user_get_cmd *cmd = vcmd->cmd;
|
||||
struct scst_user_ext_copy_remap *rcmd = &cmd->remap_cmd;
|
||||
struct scst_user_reply_cmd *reply = vcmd->reply;
|
||||
struct scst_user_ext_copy_reply_remap *rreply = &reply->remap_reply;
|
||||
static struct scst_user_ext_copy_data_descr d[1];
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_DBG("Ext copy remap cmd %d", cmd->cmd_h);
|
||||
|
||||
memset(reply, 0, sizeof(*reply));
|
||||
reply->cmd_h = cmd->cmd_h;
|
||||
reply->subcode = cmd->subcode;
|
||||
|
||||
memset(rreply, 0, sizeof(*rreply));
|
||||
|
||||
/* It's only a debug code, so it's OK to use static descr */
|
||||
|
||||
memset(d, 0, sizeof(d));
|
||||
|
||||
d[0].data_len = rcmd->data_descr.data_len;
|
||||
d[0].src_lba = rcmd->data_descr.src_lba;
|
||||
d[0].dst_lba = rcmd->data_descr.dst_lba;
|
||||
|
||||
#if 1
|
||||
rreply->remap_descriptors = (unsigned long)d;
|
||||
rreply->remap_descriptors_len = sizeof(d);
|
||||
#else
|
||||
rreply->status = SAM_STAT_CHECK_CONDITION;
|
||||
rreply->sense_len = set_sense(vcmd->sense, sizeof(vcmd->sense),
|
||||
SCST_LOAD_SENSE(scst_sense_data_protect));
|
||||
rreply->psense_buffer = (unsigned long)vcmd->sense;
|
||||
#endif
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int do_tm(struct vdisk_cmd *vcmd, int done)
|
||||
{
|
||||
struct scst_user_get_cmd *cmd = vcmd->cmd;
|
||||
@@ -663,6 +735,12 @@ static int process_cmd(struct vdisk_cmd *vcmd)
|
||||
res = do_on_free_cmd(vcmd);
|
||||
break;
|
||||
|
||||
#ifdef DEBUG_EXT_COPY_REMAP
|
||||
case SCST_USER_EXT_COPY_REMAP:
|
||||
res = do_ext_copy_remap(vcmd);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case SCST_USER_TASK_MGMT_RECEIVED:
|
||||
res = do_tm(vcmd, 0);
|
||||
break;
|
||||
@@ -699,7 +777,14 @@ void *main_loop(void *arg)
|
||||
struct vdisk_dev *dev = (struct vdisk_dev *)arg;
|
||||
struct scst_user_get_cmd cmd;
|
||||
struct scst_user_reply_cmd reply;
|
||||
struct vdisk_cmd vcmd = { -1, &cmd, dev, &reply, {0}};
|
||||
struct vdisk_cmd vcmd = {
|
||||
.fd = -1,
|
||||
.cmd = &cmd,
|
||||
.dev = dev,
|
||||
.may_need_to_free_pbuf = 0,
|
||||
.reply = &reply,
|
||||
.sense = {0}
|
||||
};
|
||||
int scst_usr_fd = dev->scst_usr_fd;
|
||||
struct pollfd pl;
|
||||
#define MULTI_CMDS_CNT 2
|
||||
@@ -741,6 +826,7 @@ void *main_loop(void *arg)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (use_multi) {
|
||||
TRACE_DBG("preplies %p (first: %p), replies_cnt %d, "
|
||||
"replies_done %d, cmds_cnt %d", (void *)multi.multi_cmd.preplies,
|
||||
@@ -901,11 +987,13 @@ static void exec_inquiry(struct vdisk_cmd *vcmd)
|
||||
uint64_t dev_id_num = gen_dev_id_num(dev);
|
||||
|
||||
if (0 == cmd->cdb[2]) { /* supported vital product data pages */
|
||||
buf[3] = 3;
|
||||
buf[3] = 5;
|
||||
buf[4] = 0x0; /* this page */
|
||||
buf[5] = 0x80; /* unit serial number */
|
||||
buf[6] = 0x83; /* device identification */
|
||||
resp_len = buf[3] + 4;
|
||||
buf[7] = 0xB0; /* block limits */
|
||||
buf[8] = 0xB1; /* block device characteristics */
|
||||
resp_len = buf[3] + 6;
|
||||
} else if (0x80 == cmd->cdb[2]) { /* unit serial number */
|
||||
int usn_len = strlen(dev->usn);
|
||||
buf[1] = 0x80;
|
||||
@@ -952,6 +1040,55 @@ static void exec_inquiry(struct vdisk_cmd *vcmd)
|
||||
buf[2] = (resp_len >> 8) & 0xFF;
|
||||
buf[3] = resp_len & 0xFF;
|
||||
resp_len += 4;
|
||||
} else if (0xB0 == cmd->cdb[2]) {
|
||||
/* Block Limits */
|
||||
int max_transfer;
|
||||
buf[1] = 0xB0;
|
||||
buf[3] = 0x3C;
|
||||
buf[5] = 0xFF; /* No MAXIMUM COMPARE AND WRITE LENGTH limit */
|
||||
/* Optimal transfer granuality is PAGE_SIZE */
|
||||
max_transfer = max((int)(4096/dev->block_size), (int)1);
|
||||
buf[6] = (max_transfer >> 8) & 0xff;
|
||||
buf[7] = max_transfer & 0xff;
|
||||
/*
|
||||
* Max transfer len is min of sg limit and 8M, but we
|
||||
* don't have access to them here, so let's use 1M.
|
||||
*/
|
||||
max_transfer = 1*1024*1024;
|
||||
buf[8] = (max_transfer >> 24) & 0xff;
|
||||
buf[9] = (max_transfer >> 16) & 0xff;
|
||||
buf[10] = (max_transfer >> 8) & 0xff;
|
||||
buf[11] = max_transfer & 0xff;
|
||||
/*
|
||||
* Let's have optimal transfer len 512KB. Better to not
|
||||
* set it at all, because we don't have such limit,
|
||||
* but some initiators may not understand that (?).
|
||||
* From other side, too big transfers are not optimal,
|
||||
* because SGV cache supports only <4M buffers.
|
||||
*/
|
||||
max_transfer = min((int)max_transfer, (int)(512*1024 / dev->block_size));
|
||||
buf[12] = (max_transfer >> 24) & 0xff;
|
||||
buf[13] = (max_transfer >> 16) & 0xff;
|
||||
buf[14] = (max_transfer >> 8) & 0xff;
|
||||
buf[15] = max_transfer & 0xff;
|
||||
resp_len = buf[3] + 4;
|
||||
} else if (0xB1 == cmd->cdb[2]) {
|
||||
int r;
|
||||
/* Block Device Characteristics */
|
||||
buf[1] = 0xB1;
|
||||
buf[3] = 0x3C;
|
||||
#if 0
|
||||
if (virt_dev->rotational) {
|
||||
#endif
|
||||
/* 15K RPM */
|
||||
r = 0x3A98;
|
||||
#if 0
|
||||
} else
|
||||
r = 1;
|
||||
#endif
|
||||
buf[4] = (r >> 8) & 0xff;
|
||||
buf[5] = r & 0xff;
|
||||
resp_len = buf[3] + 4;
|
||||
} else {
|
||||
TRACE_DBG("INQUIRY: Unsupported EVPD page %x",
|
||||
cmd->cdb[2]);
|
||||
@@ -995,7 +1132,7 @@ static void exec_inquiry(struct vdisk_cmd *vcmd)
|
||||
memcpy(address, buf, length);
|
||||
|
||||
if (length < reply->resp_data_len)
|
||||
reply->resp_data_len = length;
|
||||
set_resp_data_len(vcmd, length);
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
@@ -1019,7 +1156,7 @@ static void exec_request_sense(struct vdisk_cmd *vcmd)
|
||||
memcpy(address, b, length);
|
||||
|
||||
if (length < reply->resp_data_len)
|
||||
reply->resp_data_len = length;
|
||||
set_resp_data_len(vcmd, length);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
@@ -1260,7 +1397,7 @@ static void exec_mode_sense(struct vdisk_cmd *vcmd)
|
||||
memcpy(address, buf, offset);
|
||||
|
||||
if (offset < reply->resp_data_len)
|
||||
reply->resp_data_len = offset;
|
||||
set_resp_data_len(vcmd, offset);
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
@@ -1391,7 +1528,7 @@ static void exec_read_capacity(struct vdisk_cmd *vcmd)
|
||||
memcpy(address, buffer, length);
|
||||
|
||||
if (length < reply->resp_data_len)
|
||||
reply->resp_data_len = length;
|
||||
set_resp_data_len(vcmd, length);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
@@ -1450,7 +1587,7 @@ static void exec_read_capacity16(struct vdisk_cmd *vcmd)
|
||||
memcpy(address, buffer, length);
|
||||
|
||||
if (length < reply->resp_data_len)
|
||||
reply->resp_data_len = length;
|
||||
set_resp_data_len(vcmd, length);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
@@ -1529,7 +1666,7 @@ static void exec_read_toc(struct vdisk_cmd *vcmd)
|
||||
memcpy(address, buffer, off);
|
||||
|
||||
if (off < reply->resp_data_len)
|
||||
reply->resp_data_len = off;
|
||||
set_resp_data_len(vcmd, off);
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
@@ -1572,7 +1709,6 @@ static void exec_read(struct vdisk_cmd *vcmd, loff_t loff)
|
||||
{
|
||||
struct vdisk_dev *dev = vcmd->dev;
|
||||
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
|
||||
struct scst_user_scsi_cmd_reply_exec *reply = &vcmd->reply->exec_reply;
|
||||
int length = cmd->bufflen;
|
||||
uint8_t *address = (uint8_t*)(unsigned long)cmd->pbuf;
|
||||
int fd = vcmd->fd;
|
||||
@@ -1601,7 +1737,7 @@ static void exec_read(struct vdisk_cmd *vcmd, loff_t loff)
|
||||
PRINT_ERROR("read() returned %"PRId64" from %d (errno %d)",
|
||||
(uint64_t)err, length, errno);
|
||||
if (err == -EAGAIN)
|
||||
set_busy(reply);
|
||||
set_busy(vcmd);
|
||||
else {
|
||||
set_cmd_error(vcmd,
|
||||
SCST_LOAD_SENSE(scst_sense_read_error));
|
||||
@@ -1609,7 +1745,7 @@ static void exec_read(struct vdisk_cmd *vcmd, loff_t loff)
|
||||
goto out;
|
||||
}
|
||||
|
||||
reply->resp_data_len = cmd->bufflen;
|
||||
set_resp_data_len(vcmd, cmd->bufflen);
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
@@ -1620,7 +1756,6 @@ static void exec_write(struct vdisk_cmd *vcmd, loff_t loff)
|
||||
{
|
||||
struct vdisk_dev *dev = vcmd->dev;
|
||||
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
|
||||
struct scst_user_scsi_cmd_reply_exec *reply = &vcmd->reply->exec_reply;
|
||||
loff_t err;
|
||||
int length = cmd->bufflen;
|
||||
uint8_t *address = (uint8_t*)(unsigned long)cmd->pbuf;
|
||||
@@ -1653,7 +1788,7 @@ restart:
|
||||
PRINT_ERROR("write() returned %"PRId64" from %d (errno %d, "
|
||||
"cmd_h %x)", err, length, errno, vcmd->cmd->cmd_h);
|
||||
if (err == -EAGAIN)
|
||||
set_busy(reply);
|
||||
set_busy(vcmd);
|
||||
else {
|
||||
set_cmd_error(vcmd,
|
||||
SCST_LOAD_SENSE(scst_sense_write_error));
|
||||
@@ -1682,7 +1817,6 @@ static void exec_verify(struct vdisk_cmd *vcmd, loff_t loff)
|
||||
{
|
||||
struct vdisk_dev *dev = vcmd->dev;
|
||||
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
|
||||
struct scst_user_scsi_cmd_reply_exec *reply = &vcmd->reply->exec_reply;
|
||||
loff_t err;
|
||||
int64_t length = cmd->bufflen;
|
||||
uint8_t *address = (uint8_t *)(unsigned long)cmd->pbuf;
|
||||
@@ -1735,7 +1869,7 @@ static void exec_verify(struct vdisk_cmd *vcmd, loff_t loff)
|
||||
PRINT_ERROR("read() returned %"PRId64" from %"PRId64" "
|
||||
"(errno %d)", (uint64_t)err, len_mem, errno);
|
||||
if (err == -EAGAIN)
|
||||
set_busy(reply);
|
||||
set_busy(vcmd);
|
||||
else {
|
||||
set_cmd_error(vcmd,
|
||||
SCST_LOAD_SENSE(scst_sense_read_error));
|
||||
@@ -1761,3 +1895,34 @@ out:
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
static void exec_write_same(struct vdisk_cmd *vcmd)
|
||||
{
|
||||
struct vdisk_dev *dev = vcmd->dev;
|
||||
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
|
||||
struct scst_user_scsi_cmd_reply_exec *reply = &vcmd->reply->exec_reply;
|
||||
uint64_t blocks = cmd->data_len >> dev->block_shift;
|
||||
static struct scst_user_data_descriptor uwhere[3];
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (blocks < 2)
|
||||
goto out;
|
||||
|
||||
/* It's only a debug code, so it's OK to use static descr */
|
||||
|
||||
memset(uwhere, 0, sizeof(uwhere));
|
||||
|
||||
uwhere[0].usdd_lba = cmd->lba;
|
||||
uwhere[0].usdd_blocks = 1;
|
||||
uwhere[1].usdd_lba = cmd->lba+1;
|
||||
uwhere[1].usdd_blocks = blocks-1;
|
||||
|
||||
reply->reply_type = SCST_EXEC_REPLY_DO_WRITE_SAME;
|
||||
reply->ws_descriptors_len = sizeof(uwhere);
|
||||
reply->ws_descriptors = (unsigned long)uwhere;
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
+6
-1
@@ -23,10 +23,14 @@
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#ifndef WRITE_SAME_10
|
||||
#define WRITE_SAME_10 0x41
|
||||
#endif
|
||||
|
||||
/* 8 byte ASCII Vendor */
|
||||
#define VENDOR "SCST_USR"
|
||||
/* 4 byte ASCII Product Revision Level - left aligned */
|
||||
#define FIO_REV " 310"
|
||||
#define FIO_REV " 320"
|
||||
|
||||
#define MAX_USN_LEN (20+1) /* For '\0' */
|
||||
|
||||
@@ -93,6 +97,7 @@ struct vdisk_cmd
|
||||
int fd;
|
||||
struct scst_user_get_cmd *cmd;
|
||||
struct vdisk_dev *dev;
|
||||
unsigned int may_need_to_free_pbuf:1;
|
||||
struct scst_user_reply_cmd *reply;
|
||||
uint8_t sense[SCST_SENSE_BUFFERSIZE];
|
||||
};
|
||||
|
||||
+1
-148
@@ -1,148 +1 @@
|
||||
/*
|
||||
* debug.c
|
||||
*
|
||||
* Copyright (C) 2004 - 2015 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
* Copyright (C) 2004 - 2005 Leonid Stoljar
|
||||
* Copyright (C) 2007 - 2015 SanDisk Corporation
|
||||
*
|
||||
* Contains helper functions for execution tracing and error reporting.
|
||||
* Intended to be included in main .c file.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, version 2
|
||||
* of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
pid_t gettid (void)
|
||||
{
|
||||
return syscall(__NR_gettid);
|
||||
}
|
||||
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
|
||||
#define TRACE_BUF_SIZE 512
|
||||
|
||||
static char trace_buf[TRACE_BUF_SIZE];
|
||||
static pthread_spinlock_t trace_buf_lock;
|
||||
|
||||
int debug_print_prefix(unsigned long trace_flag, const char *prefix,
|
||||
const char *func, int line)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
pthread_spin_lock(&trace_buf_lock);
|
||||
|
||||
if (trace_flag & TRACE_TIME) {
|
||||
struct tm t;
|
||||
time_t tt;
|
||||
time(&tt);
|
||||
localtime_r(&tt, &t);
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%d:%d:%d ",
|
||||
t.tm_hour, t.tm_min, t.tm_sec);
|
||||
}
|
||||
if (trace_flag & TRACE_PID)
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "[%d]: ",
|
||||
gettid());
|
||||
if (prefix != NULL)
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%s:", prefix);
|
||||
if (trace_flag & TRACE_FUNCTION)
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%s:", func);
|
||||
if (trace_flag & TRACE_LINE)
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%i:", line);
|
||||
|
||||
if (i > 0)
|
||||
PRINTN("%s", trace_buf);
|
||||
|
||||
pthread_spin_unlock(&trace_buf_lock);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
void debug_print_buffer(const void *data, int len)
|
||||
{
|
||||
int z, z1, i;
|
||||
const unsigned char *buf = (const unsigned char *) data;
|
||||
int f = 0;
|
||||
|
||||
if (buf == NULL)
|
||||
return;
|
||||
|
||||
pthread_spin_lock(&trace_buf_lock);
|
||||
|
||||
PRINT(" (h)___0__1__2__3__4__5__6__7__8__9__A__B__C__D__E__F");
|
||||
for (z = 0, z1 = 0, i = 0; z < len; z++) {
|
||||
if (z % 16 == 0) {
|
||||
if (z != 0) {
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i,
|
||||
" ");
|
||||
for (; (z1 < z) && (i < TRACE_BUF_SIZE - 1);
|
||||
z1++) {
|
||||
if ((buf[z1] >= 0x20) &&
|
||||
(buf[z1] < 0x80))
|
||||
trace_buf[i++] = buf[z1];
|
||||
else
|
||||
trace_buf[i++] = '.';
|
||||
}
|
||||
trace_buf[i] = '\0';
|
||||
PRINT("%s", trace_buf);
|
||||
i = 0;
|
||||
f = 1;
|
||||
}
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i,
|
||||
"%4x: ", z);
|
||||
}
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%02x ",
|
||||
buf[z]);
|
||||
}
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, " ");
|
||||
for (; (z1 < z) && (i < TRACE_BUF_SIZE - 1); z1++) {
|
||||
if ((buf[z1] > 0x20) && (buf[z1] < 0x80))
|
||||
trace_buf[i++] = buf[z1];
|
||||
else
|
||||
trace_buf[i++] = '.';
|
||||
}
|
||||
trace_buf[i] = '\0';
|
||||
if (f) {
|
||||
PRINT("%s", trace_buf)
|
||||
} else {
|
||||
PRINT("%s", trace_buf);
|
||||
}
|
||||
|
||||
pthread_spin_unlock(&trace_buf_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
int debug_init(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = pthread_spin_init(&trace_buf_lock, PTHREAD_PROCESS_PRIVATE);
|
||||
if (res != 0) {
|
||||
res = errno;
|
||||
PRINT_ERROR("pthread_spin_init() failed: %s", strerror(res));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void debug_done(void)
|
||||
{
|
||||
pthread_spin_destroy(&trace_buf_lock);
|
||||
}
|
||||
|
||||
#endif /* DEBUG || TRACING */
|
||||
#include "../include/debug.c"
|
||||
|
||||
+7
-1
@@ -25,6 +25,7 @@
|
||||
#include <stdint.h>
|
||||
#include <getopt.h>
|
||||
#include <malloc.h>
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
@@ -36,7 +37,9 @@
|
||||
|
||||
char *app_name;
|
||||
|
||||
#include "version.h"
|
||||
#include "common.h"
|
||||
#include "debug.h"
|
||||
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
|
||||
@@ -62,11 +65,11 @@ char *app_name;
|
||||
# endif
|
||||
#endif /* DEBUG */
|
||||
|
||||
bool log_daemon = false;
|
||||
unsigned long trace_flag = DEFAULT_LOG_FLAGS;
|
||||
#endif /* defined(DEBUG) || defined(TRACING) */
|
||||
|
||||
#define DEF_BLOCK_SHIFT 9
|
||||
#define VERSION_STR "3.1.0-pre1"
|
||||
#define THREADS 7
|
||||
|
||||
#define MAX_VDEVS 10
|
||||
@@ -391,6 +394,9 @@ int start(int argc, char **argv)
|
||||
desc.opt.queue_alg = SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER;
|
||||
desc.opt.qerr = SCST_QERR_0_ALL_RESUME;
|
||||
desc.opt.d_sense = SCST_D_SENSE_0_FIXED_SENSE;
|
||||
#ifdef DEBUG_EXT_COPY_REMAP
|
||||
desc.opt.ext_copy_remap_supported = 1;
|
||||
#endif
|
||||
|
||||
res = ioctl(devs[i].scst_usr_fd, SCST_USER_REGISTER_DEVICE, &desc);
|
||||
if (res != 0) {
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* debug.c
|
||||
*
|
||||
* Copyright (C) 2004 - 2015 Vladislav Bolkhovitin <vst@vlnb.net>
|
||||
* Copyright (C) 2004 - 2005 Leonid Stoljar
|
||||
* Copyright (C) 2007 - 2015 SanDisk Corporation
|
||||
*
|
||||
* Contains helper functions for execution tracing and error reporting.
|
||||
* Intended to be included in main .c file.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, version 2
|
||||
* of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
pid_t gettid(void)
|
||||
{
|
||||
return syscall(__NR_gettid);
|
||||
}
|
||||
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
|
||||
#define TRACE_BUF_SIZE 512
|
||||
|
||||
static char trace_buf[TRACE_BUF_SIZE];
|
||||
static pthread_spinlock_t trace_buf_lock;
|
||||
|
||||
int debug_print_prefix(unsigned long trace_flag, const char *prefix,
|
||||
const char *func, int line)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
pthread_spin_lock(&trace_buf_lock);
|
||||
|
||||
if (trace_flag & TRACE_TIME) {
|
||||
struct tm t;
|
||||
time_t tt;
|
||||
|
||||
time(&tt);
|
||||
localtime_r(&tt, &t);
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%d:%d:%d ",
|
||||
t.tm_hour, t.tm_min, t.tm_sec);
|
||||
}
|
||||
if (trace_flag & TRACE_PID)
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "[%d]: ",
|
||||
gettid());
|
||||
if (prefix != NULL)
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%s:", prefix);
|
||||
if (trace_flag & TRACE_FUNCTION)
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%s:", func);
|
||||
if (trace_flag & TRACE_LINE)
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%i:", line);
|
||||
|
||||
if (i > 0)
|
||||
PRINTN(LOG_INFO, "%s", trace_buf);
|
||||
|
||||
pthread_spin_unlock(&trace_buf_lock);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
void debug_print_buffer(const void *data, int len)
|
||||
{
|
||||
int z, z1, i;
|
||||
const unsigned char *buf = (const unsigned char *) data;
|
||||
int f = 0;
|
||||
|
||||
if (buf == NULL)
|
||||
return;
|
||||
|
||||
pthread_spin_lock(&trace_buf_lock);
|
||||
|
||||
PRINT(LOG_INFO, " (h)___0__1__2__3__4__5__6__7__8__9__A__B__C__D__E__F");
|
||||
for (z = 0, z1 = 0, i = 0; z < len; z++) {
|
||||
if (z % 16 == 0) {
|
||||
if (z != 0) {
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i,
|
||||
" ");
|
||||
for (; (z1 < z) && (i < TRACE_BUF_SIZE - 1);
|
||||
z1++) {
|
||||
if ((buf[z1] >= 0x20) &&
|
||||
(buf[z1] < 0x80))
|
||||
trace_buf[i++] = buf[z1];
|
||||
else
|
||||
trace_buf[i++] = '.';
|
||||
}
|
||||
trace_buf[i] = '\0';
|
||||
PRINT(LOG_INFO, "%s", trace_buf);
|
||||
i = 0;
|
||||
f = 1;
|
||||
}
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i,
|
||||
"%4x: ", z);
|
||||
}
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%02x ",
|
||||
buf[z]);
|
||||
}
|
||||
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, " ");
|
||||
for (; (z1 < z) && (i < TRACE_BUF_SIZE - 1); z1++) {
|
||||
if ((buf[z1] > 0x20) && (buf[z1] < 0x80))
|
||||
trace_buf[i++] = buf[z1];
|
||||
else
|
||||
trace_buf[i++] = '.';
|
||||
}
|
||||
trace_buf[i] = '\0';
|
||||
if (f)
|
||||
PRINT(LOG_INFO, "%s", trace_buf);
|
||||
else
|
||||
PRINT(LOG_INFO, "%s", trace_buf);
|
||||
|
||||
pthread_spin_unlock(&trace_buf_lock);
|
||||
}
|
||||
|
||||
int debug_init(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = pthread_spin_init(&trace_buf_lock, PTHREAD_PROCESS_PRIVATE);
|
||||
if (res != 0) {
|
||||
res = errno;
|
||||
PRINT_ERROR("pthread_spin_init() failed: %s", strerror(res));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void debug_done(void)
|
||||
{
|
||||
pthread_spin_destroy(&trace_buf_lock);
|
||||
}
|
||||
|
||||
#endif /* DEBUG || TRACING */
|
||||
@@ -24,6 +24,8 @@
|
||||
#include <sys/types.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <errno.h>
|
||||
#include <syslog.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
extern pid_t gettid(void);
|
||||
|
||||
@@ -55,8 +57,21 @@ extern pid_t gettid(void);
|
||||
#define TRACE_ORDER 0x00008000
|
||||
#define TRACE_ALL 0xffffffff
|
||||
|
||||
#define PRINT(format, args...) fprintf(stdout, format "\n", ## args);
|
||||
#define PRINTN(format, args...) fprintf(stdout, format, ## args);
|
||||
#define PRINT(priority, format, args...) \
|
||||
do { \
|
||||
if (log_daemon) \
|
||||
syslog(priority, format "\n", ## args); \
|
||||
else \
|
||||
fprintf(stdout, format "\n", ## args); \
|
||||
} while (0)
|
||||
|
||||
#define PRINTN(priority, format, args...) \
|
||||
do { \
|
||||
if (log_daemon) \
|
||||
syslog(priority, format, ## args); \
|
||||
else \
|
||||
fprintf(stdout, format, ## args); \
|
||||
} while (0)
|
||||
|
||||
extern char *app_name;
|
||||
#define LOG_PREFIX app_name
|
||||
@@ -69,6 +84,7 @@ extern char *app_name;
|
||||
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
|
||||
extern bool log_daemon;
|
||||
extern unsigned long trace_flag;
|
||||
|
||||
extern int debug_init(void);
|
||||
@@ -80,24 +96,23 @@ extern void debug_done(void);
|
||||
*/
|
||||
|
||||
extern int debug_print_prefix(unsigned long trace_flag, const char *prefix,
|
||||
const char *func, int line);
|
||||
const char *func, int line);
|
||||
extern void debug_print_buffer(const void *data, int len);
|
||||
|
||||
#define TRACE(trace, format, args...) \
|
||||
do { \
|
||||
if (trace_flag & (trace)) \
|
||||
{ \
|
||||
debug_print_prefix(trace_flag, __LOG_PREFIX, __FUNCTION__, \
|
||||
__LINE__); \
|
||||
PRINT(format, args); \
|
||||
} \
|
||||
} while(0)
|
||||
if (trace_flag & (trace)) { \
|
||||
debug_print_prefix(trace_flag, __LOG_PREFIX, \
|
||||
__func__, __LINE__); \
|
||||
PRINT(LOG_DEBUG, format, args); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PRINT_BUFFER(message, buff, len) \
|
||||
do { \
|
||||
PRINT("%s:", message); \
|
||||
debug_print_buffer(buff, len); \
|
||||
} while(0)
|
||||
PRINT(LOG_INFO, "%s:", message); \
|
||||
debug_print_buffer(buff, len); \
|
||||
} while (0)
|
||||
|
||||
#else /* DEBUG || TRACING */
|
||||
|
||||
@@ -115,142 +130,126 @@ static inline void debug_done(void) {}
|
||||
|
||||
#define TRACE_MEM(format, args...) \
|
||||
do { \
|
||||
if (trace_flag & TRACE_MEMORY) \
|
||||
{ \
|
||||
debug_print_prefix(trace_flag, NULL, __FUNCTION__, \
|
||||
__LINE__); \
|
||||
PRINT(format, args); \
|
||||
} \
|
||||
} while(0)
|
||||
if (trace_flag & TRACE_MEMORY) { \
|
||||
debug_print_prefix(trace_flag, NULL, \
|
||||
__func__, __LINE__); \
|
||||
PRINT(LOG_DEBUG, format, args); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TRACE_DBG(format, args...) \
|
||||
do { \
|
||||
if (trace_flag & TRACE_DEBUG) \
|
||||
{ \
|
||||
debug_print_prefix(trace_flag, NULL, __FUNCTION__, \
|
||||
__LINE__); \
|
||||
PRINT(format, args); \
|
||||
} \
|
||||
} while(0)
|
||||
if (trace_flag & TRACE_DEBUG) { \
|
||||
debug_print_prefix(trace_flag, NULL, \
|
||||
__func__, __LINE__); \
|
||||
PRINT(LOG_DEBUG, format, args); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TRACE_DBG_SPECIAL(args...) TRACE(TRACE_DEBUG|TRACE_SPECIAL, args)
|
||||
|
||||
#define TRACE_MGMT_DBG(format, args...) \
|
||||
do { \
|
||||
if (trace_flag & TRACE_MGMT_DEBUG) \
|
||||
{ \
|
||||
debug_print_prefix(trace_flag, NULL, __FUNCTION__, \
|
||||
__LINE__); \
|
||||
PRINT(format, args); \
|
||||
} \
|
||||
} while(0)
|
||||
if (trace_flag & TRACE_MGMT_DEBUG) { \
|
||||
debug_print_prefix(trace_flag, NULL, \
|
||||
__func__, __LINE__); \
|
||||
PRINT(LOG_DEBUG, format, args); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TRACE_BUFFER(message, buff, len) \
|
||||
do { \
|
||||
if (trace_flag & TRACE_BUFF) \
|
||||
{ \
|
||||
debug_print_prefix(trace_flag, NULL, __FUNCTION__, \
|
||||
__LINE__); \
|
||||
PRINT("%s:", message); \
|
||||
debug_print_buffer(buff, len); \
|
||||
} \
|
||||
} while(0)
|
||||
if (trace_flag & TRACE_BUFF) { \
|
||||
debug_print_prefix(trace_flag, NULL, \
|
||||
__func__, __LINE__); \
|
||||
PRINT(LOG_DEBUG, "%s:", message); \
|
||||
debug_print_buffer(buff, len); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TRACE_BUFF_FLAG(flag, message, buff, len) \
|
||||
do { \
|
||||
if (trace_flag & (flag)) \
|
||||
{ \
|
||||
debug_print_prefix(trace_flag, NULL, __FUNCTION__, \
|
||||
__LINE__); \
|
||||
PRINT("%s:", message); \
|
||||
debug_print_buffer(buff, len); \
|
||||
} \
|
||||
} while(0)
|
||||
if (trace_flag & (flag)) { \
|
||||
debug_print_prefix(trace_flag, NULL, \
|
||||
__func__, __LINE__); \
|
||||
PRINT(LOG_DEBUG, "%s:", message); \
|
||||
debug_print_buffer(buff, len); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PRINT_INFO(format, args...) \
|
||||
do { \
|
||||
debug_print_prefix(trace_flag, __LOG_PREFIX, __FUNCTION__, \
|
||||
__LINE__); \
|
||||
PRINT(format, args); \
|
||||
} while(0)
|
||||
debug_print_prefix(trace_flag, __LOG_PREFIX, \
|
||||
__func__, __LINE__); \
|
||||
PRINT(LOG_INFO, format, args); \
|
||||
} while (0)
|
||||
|
||||
#define PRINT_WARNING(format, args...) \
|
||||
do { \
|
||||
debug_print_prefix(trace_flag, __LOG_PREFIX, __FUNCTION__, \
|
||||
__LINE__); \
|
||||
PRINT("***WARNING*** " format, args); \
|
||||
} while(0)
|
||||
debug_print_prefix(trace_flag, __LOG_PREFIX, \
|
||||
__func__, __LINE__); \
|
||||
PRINT(LOG_WARNING, "***WARNING*** " format, args); \
|
||||
} while (0)
|
||||
|
||||
#define PRINT_ERROR(format, args...) \
|
||||
do { \
|
||||
debug_print_prefix(trace_flag, __LOG_PREFIX, __FUNCTION__, \
|
||||
__LINE__); \
|
||||
PRINT("***ERROR*** " format, args); \
|
||||
} while(0)
|
||||
debug_print_prefix(trace_flag, __LOG_PREFIX, \
|
||||
__func__, __LINE__); \
|
||||
PRINT(LOG_ERR, "***ERROR*** " format, args); \
|
||||
} while (0)
|
||||
|
||||
#define TRACE_ENTRY() \
|
||||
do { \
|
||||
if (trace_flag & TRACE_ENTRYEXIT) \
|
||||
{ \
|
||||
if (trace_flag & TRACE_PID) \
|
||||
{ \
|
||||
PRINT("[%d]: ENTRY %s", gettid(), \
|
||||
__FUNCTION__); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
PRINT("ENTRY %s", __FUNCTION__); \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
if (trace_flag & TRACE_ENTRYEXIT) { \
|
||||
if (trace_flag & TRACE_PID) { \
|
||||
PRINT(LOG_DEBUG, "[%d]: ENTRY %s", \
|
||||
gettid(), __func__); \
|
||||
} else { \
|
||||
PRINT(LOG_DEBUG, "ENTRY %s", \
|
||||
__func__); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TRACE_EXIT() \
|
||||
do { \
|
||||
if (trace_flag & TRACE_ENTRYEXIT) \
|
||||
{ \
|
||||
if (trace_flag & TRACE_PID) \
|
||||
{ \
|
||||
PRINT("[%d]: EXIT %s", gettid(), \
|
||||
__FUNCTION__); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
PRINT("EXIT %s", __FUNCTION__); \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
if (trace_flag & TRACE_ENTRYEXIT) { \
|
||||
if (trace_flag & TRACE_PID) { \
|
||||
PRINT(LOG_DEBUG, "[%d]: EXIT %s", \
|
||||
gettid(), __func__); \
|
||||
} else { \
|
||||
PRINT(LOG_DEBUG, "EXIT %s", __func__); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TRACE_EXIT_RES(res) \
|
||||
do { \
|
||||
if (trace_flag & TRACE_ENTRYEXIT) \
|
||||
{ \
|
||||
if (trace_flag & TRACE_PID) \
|
||||
{ \
|
||||
PRINT("[%d]: EXIT %s: %ld", gettid(), \
|
||||
__FUNCTION__, (long)(res)); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
PRINT("EXIT %s: %ld", __FUNCTION__, (long)(res)); \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
if (trace_flag & TRACE_ENTRYEXIT) { \
|
||||
if (trace_flag & TRACE_PID) { \
|
||||
PRINT(LOG_DEBUG, "[%d]: EXIT %s: %ld", \
|
||||
gettid(), __func__, \
|
||||
(long)(res)); \
|
||||
} else { \
|
||||
PRINT(LOG_DEBUG, "EXIT %s: %ld", \
|
||||
__func__, (long)(res)); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TRACE_EXIT_HRES(res) \
|
||||
do { \
|
||||
if (trace_flag & TRACE_ENTRYEXIT) \
|
||||
{ \
|
||||
if (trace_flag & TRACE_PID) \
|
||||
{ \
|
||||
PRINT("[%d]: EXIT %s: 0x%lx", gettid(), \
|
||||
__FUNCTION__, (long)(res)); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
PRINT("EXIT %s: %lx", __FUNCTION__, (long)(res)); \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
if (trace_flag & TRACE_ENTRYEXIT) { \
|
||||
if (trace_flag & TRACE_PID) { \
|
||||
PRINT(LOG_DEBUG, "[%d]: EXIT %s: 0x%lx",\
|
||||
gettid(), __func__, \
|
||||
(long)(res)); \
|
||||
} else { \
|
||||
PRINT(LOG_DEBUG, "EXIT %s: %lx", \
|
||||
__func__, (long)(res)); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#else /* DEBUG */
|
||||
|
||||
@@ -271,39 +270,25 @@ do { \
|
||||
#ifdef LOG_PREFIX
|
||||
|
||||
#define PRINT_INFO(format, args...) \
|
||||
do { \
|
||||
PRINT("%s: " format, LOG_PREFIX, args); \
|
||||
} while(0)
|
||||
PRINT(LOG_INFO, "%s: " format, LOG_PREFIX, args) \
|
||||
|
||||
#define PRINT_WARNING(format, args...) \
|
||||
do { \
|
||||
PRINT("%s: ***WARNING*** " \
|
||||
format, LOG_PREFIX, args); \
|
||||
} while(0)
|
||||
PRINT(LOG_WARNING, "%s: ***WARNING*** " format, \
|
||||
LOG_PREFIX, args) \
|
||||
|
||||
#define PRINT_ERROR(format, args...) \
|
||||
do { \
|
||||
PRINT("%s: ***ERROR*** " \
|
||||
format, LOG_PREFIX, args); \
|
||||
} while(0)
|
||||
PRINT(LOG_ERR, "%s: ***ERROR*** " format, \
|
||||
LOG_PREFIX, args) \
|
||||
|
||||
#else
|
||||
|
||||
#define PRINT_INFO(format, args...) \
|
||||
do { \
|
||||
PRINT(format, args); \
|
||||
} while(0)
|
||||
#define PRINT_INFO(format, args...) PRINT(LOG_INFO, format, args)
|
||||
|
||||
#define PRINT_WARNING(format, args...) \
|
||||
do { \
|
||||
PRINT("***WARNING*** " format, args); \
|
||||
} while(0)
|
||||
|
||||
PRINT(LOG_WARNING, "***WARNING*** " format, args) \
|
||||
|
||||
#define PRINT_ERROR(format, args...) \
|
||||
do { \
|
||||
PRINT("***ERROR*** " format, args); \
|
||||
} while(0)
|
||||
PRINT(LOG_ERR, "***ERROR*** " format, args) \
|
||||
|
||||
#endif /* LOG_PREFIX */
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* version.h
|
||||
*
|
||||
* Copyright (C) 2015 SanDisk Corporation
|
||||
*
|
||||
* Contains macroses for execution tracing and error reporting
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, version 2
|
||||
* of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __VERSION_H
|
||||
#define __VERSION_H
|
||||
|
||||
#define VERSION_STR "3.2.0-pre1"
|
||||
|
||||
#endif /* __VERSION_H */
|
||||
@@ -0,0 +1,104 @@
|
||||
ifndef PREFIX
|
||||
PREFIX=/usr/local
|
||||
endif
|
||||
|
||||
SHELL=/bin/bash
|
||||
|
||||
SRCS_F = stpgd_main.c debug.c
|
||||
OBJS_F = $(SRCS_F:.c=.o)
|
||||
|
||||
#SRCS_C =
|
||||
#OBJS_C = $(SRCS_C:.c=.o)
|
||||
|
||||
SCST_INC_DIR := $(shell if [ -e "$$PWD/../../scst" ]; \
|
||||
then echo "$$PWD/../../scst/include"; \
|
||||
else echo "$(DESTDIR)$(PREFIX)/include/scst"; fi)
|
||||
DEBUG_INC_DIR := ../include
|
||||
INSTALL_DIR := $(DESTDIR)$(PREFIX)/sbin
|
||||
ON_STPG := $(DESTDIR)$(PREFIX)/bin/scst/scst_on_stpg
|
||||
|
||||
CFLAGS += -O2 -Wall -Wextra -Wno-unused-parameter -Wstrict-prototypes \
|
||||
-I$(SCST_INC_DIR) -I$(DEBUG_INC_DIR) -D_GNU_SOURCE -D__USE_FILE_OFFSET64 \
|
||||
-D__USE_LARGEFILE64
|
||||
PROGS = stpgd
|
||||
LIBS = -lpthread
|
||||
|
||||
CFLAGS += -DEXTRACHECKS
|
||||
#CFLAGS += -DTRACING
|
||||
CFLAGS += -DDEBUG -g -fno-inline -fno-inline-functions
|
||||
CFLAGS += -W -Wno-unused-parameter
|
||||
CFLAGS += $(LOCAL_CFLAGS)
|
||||
|
||||
#CFLAGS += -DDEBUG_NOMEM
|
||||
|
||||
all: $(PROGS)
|
||||
|
||||
stpgd: .depend_f $(OBJS_F)
|
||||
$(CC) $(OBJS_F) $(LIBS) $(LOCAL_LD_FLAGS) -o $@
|
||||
|
||||
#cdrom_tgt: .depend_c $(OBJS_C)
|
||||
# $(CC) $(OBJS_C) $(LIBS) $(LOCAL_LD_FLAGS) -o $@
|
||||
|
||||
ifeq (.depend_f,$(wildcard .depend_f))
|
||||
-include .depend_f
|
||||
endif
|
||||
|
||||
#ifeq (.depend_c,$(wildcard .depend_c))
|
||||
#-include .depend_c
|
||||
#endif
|
||||
|
||||
%.o: %.c Makefile
|
||||
$(CC) -c -o $(@) $(CFLAGS) $(<)
|
||||
|
||||
.depend_f:
|
||||
$(CC) -M $(CFLAGS) $(SRCS_F) >$(@)
|
||||
|
||||
#.depend_c:
|
||||
# $(CC) -M $(CFLAGS) $(SRCS_C) >$(@)
|
||||
|
||||
install: all
|
||||
install -d $(INSTALL_DIR)
|
||||
install -m 700 $(PROGS) $(INSTALL_DIR)
|
||||
install -m 700 scst_on_stpg $(ON_STPG)
|
||||
|
||||
uninstall:
|
||||
rm -f $(INSTALL_DIR)/$(PROGS)
|
||||
rm -rf $(INSTALL_DIR)
|
||||
|
||||
clean:
|
||||
rm -f *.o $(PROGS) .depend*
|
||||
|
||||
extraclean: clean
|
||||
rm -f *.orig *.rej
|
||||
|
||||
2release:
|
||||
sed -i.aa s/"^C\?FLAGS += \-DEXTRACHECKS"/"#CFLAGS += \-DEXTRACHECKS"/ Makefile
|
||||
grep "^#CFLAGS += \-DEXTRACHECKS" Makefile >/dev/null
|
||||
sed -i.aa s/"^#\?CFLAGS += \-DTRACING"/"CFLAGS += \-DTRACING"/ Makefile
|
||||
grep "^CFLAGS += \-DTRACING" Makefile >/dev/null
|
||||
sed -i.aa s/"^C\?FLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/"#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/ Makefile
|
||||
grep "^#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions" Makefile >/dev/null
|
||||
rm Makefile.aa
|
||||
|
||||
2debug:
|
||||
sed -i.aa s/"^#\?CFLAGS += \-DEXTRACHECKS"/"CFLAGS += \-DEXTRACHECKS"/ Makefile
|
||||
grep "^CFLAGS += \-DEXTRACHECKS" Makefile >/dev/null
|
||||
sed -i.aa s/"^C\?FLAGS += \-DTRACING"/"#CFLAGS += \-DTRACING"/ Makefile
|
||||
grep "^#CFLAGS += \-DTRACING" Makefile >/dev/null
|
||||
sed -i.aa s/"^#\?CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/"CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/ Makefile
|
||||
grep "^CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions" Makefile >/dev/null
|
||||
rm Makefile.aa
|
||||
|
||||
2perf:
|
||||
sed -i.aa s/"^C\?FLAGS += \-DEXTRACHECKS"/"#CFLAGS += \-DEXTRACHECKS"/ Makefile
|
||||
grep "^#CFLAGS += \-DEXTRACHECKS" Makefile >/dev/null
|
||||
sed -i.aa s/"^C\?FLAGS += \-DTRACING"/"#CFLAGS += \-DTRACING"/ Makefile
|
||||
grep "^#CFLAGS += \-DTRACING" Makefile >/dev/null
|
||||
sed -i.aa s/"^C\?FLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/"#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/ Makefile
|
||||
grep "^#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions" Makefile >/dev/null
|
||||
rm Makefile.aa
|
||||
|
||||
release-archive:
|
||||
../../scripts/generate-release-archive stpgd "$$(sed -n 's/^#define[[:blank:]]VERSION_STR[[:blank:]]*\"\([^\"]*\)\".*/\1/p' stpgd_main.c)"
|
||||
|
||||
.PHONY: all install uninstall clean extraclean 2release 2debug 2perf
|
||||
@@ -0,0 +1,9 @@
|
||||
This is stpgd service called by the SCST core upon receiving SET TARGET
|
||||
PORT GROUPS command via SCST events subsystem. In turn, stpgd calls
|
||||
external script to actually modify ALUA state as requested by the SET
|
||||
TARGET PORT GROUPS command via cluster manager and SCST sysfs. You can
|
||||
find example of such script in scst_on_stpg file.
|
||||
|
||||
Reason why such dual stage approach is used is, because there is no way
|
||||
from inside the kernel to control execution of external programs and
|
||||
there is no way to write a service calling IOCTLs on shell.
|
||||
@@ -0,0 +1 @@
|
||||
#include "../include/debug.c"
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Script invoked by SCST for processing a SCSI SET TARGET PORT GROUPS command
|
||||
#
|
||||
# Arguments: SCST_DEVICE_NAME (device name where this STPG command was
|
||||
# received on), SCST_PREV_ALUA_STATE, SCST_ALUA_STATE,
|
||||
# SCST_DEVICE_GROUP and SCST_TARGET_GROUP + environmental variables
|
||||
# with the same names set
|
||||
#
|
||||
|
||||
echo $SCST_ALUA_STATE >/sys/kernel/scst_tgt/device_groups/$SCST_DEVICE_GROUP/target_groups/$SCST_TARGET_GROUP/state
|
||||
@@ -0,0 +1,562 @@
|
||||
/*
|
||||
* stpgd_main.c
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <malloc.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <syslog.h>
|
||||
#include <signal.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "version.h"
|
||||
#include "debug.h"
|
||||
#include "scst_event.h"
|
||||
|
||||
char *app_name;
|
||||
|
||||
#define DEFAULT_TRANSITION_TIME 17
|
||||
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#define DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MINOR | TRACE_PID | \
|
||||
TRACE_FUNCTION | TRACE_SPECIAL | TRACE_MGMT | TRACE_MGMT_DEBUG | \
|
||||
TRACE_TIME)
|
||||
|
||||
#define TRACE_SN(args...) TRACE(TRACE_SCSI_SERIALIZING, args)
|
||||
|
||||
#else /* DEBUG */
|
||||
|
||||
# ifdef TRACING
|
||||
#define DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MGMT | \
|
||||
TRACE_TIME | TRACE_SPECIAL)
|
||||
# else
|
||||
#define DEFAULT_LOG_FLAGS 0
|
||||
# endif
|
||||
#endif /* DEBUG */
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
|
||||
|
||||
bool log_daemon = true;
|
||||
unsigned long trace_flag = DEFAULT_LOG_FLAGS;
|
||||
#endif /* defined(DEBUG) || defined(TRACING) */
|
||||
|
||||
int transition_timeout = DEFAULT_TRANSITION_TIME;
|
||||
|
||||
static struct option const long_options[] = {
|
||||
{"path", required_argument, 0, 'p'},
|
||||
{"timeout", required_argument, 0, 't'},
|
||||
{"foreground", no_argument, 0, 'f'},
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
{"debug", required_argument, 0, 'd'},
|
||||
#endif
|
||||
{"version", no_argument, 0, 'v'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
int stpg_init_report_pipe[2];
|
||||
char *stpg_path;
|
||||
|
||||
static void usage(int status)
|
||||
{
|
||||
if (status != 0)
|
||||
fprintf(stderr, "Try '%s --help' for more information.\n", app_name);
|
||||
else {
|
||||
printf("Usage: %s [OPTIONS]\n", app_name);
|
||||
printf("STPG target daemon\n");
|
||||
printf(" -f, --foreground make the program run in the foreground\n");
|
||||
printf(" -p, --path absolute path to the STPG script\n");
|
||||
printf(" -t, --timeout transition timeout\n");
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
printf(" -d, --debug=level debug tracing level\n");
|
||||
#endif
|
||||
printf(" -h, --help display this help and exit\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void stpg_handle_tm_received(struct scst_event_user *event_user)
|
||||
{
|
||||
/*
|
||||
* Put code to abort state transition here, if this STPG cmd,
|
||||
* identified by cmd_to_abort_tag or RESET, requested to be aborted
|
||||
*/
|
||||
}
|
||||
|
||||
int invoke_stpg(const uint8_t *device_name,
|
||||
const struct scst_event_stpg_descr *descr, pid_t *out_pid)
|
||||
{
|
||||
char *args[7], *env[7];
|
||||
int res = 0, ret, i;
|
||||
pid_t c_pid;
|
||||
|
||||
args[0] = stpg_path;
|
||||
args[1] = (char *)device_name;
|
||||
args[2] = (char *)descr->prev_state;
|
||||
args[3] = (char *)descr->new_state;
|
||||
args[4] = (char *)descr->dg_name;
|
||||
args[5] = (char *)descr->tg_name;
|
||||
args[6] = NULL;
|
||||
|
||||
env[0] = "PATH=/bin:/usr/bin:/sbin:/usr/sbin";
|
||||
ret = asprintf(&env[1], "SCST_DEVICE_NAME=%s", device_name);
|
||||
if (ret < 0) {
|
||||
res = -errno;
|
||||
PRINT_ERROR("asprintf() failed: %d (%s)", res, strerror(-res));
|
||||
goto out;
|
||||
}
|
||||
ret = asprintf(&env[2], "SCST_PREV_ALUA_STATE=%s", descr->prev_state);
|
||||
if (ret < 0) {
|
||||
res = -errno;
|
||||
PRINT_ERROR("asprintf() failed: %d (%s)", res, strerror(-res));
|
||||
goto out;
|
||||
}
|
||||
ret = asprintf(&env[3], "SCST_ALUA_STATE=%s", descr->new_state);
|
||||
if (ret < 0) {
|
||||
res = -errno;
|
||||
PRINT_ERROR("asprintf() failed: %d (%s)", res, strerror(-res));
|
||||
goto out;
|
||||
}
|
||||
ret = asprintf(&env[4], "SCST_DEVICE_GROUP=%s", descr->dg_name);
|
||||
if (ret < 0) {
|
||||
res = -errno;
|
||||
PRINT_ERROR("asprintf() failed: %d (%s)", res, strerror(-res));
|
||||
goto out;
|
||||
}
|
||||
ret = asprintf(&env[5], "SCST_TARGET_GROUP=%s", descr->tg_name);
|
||||
if (ret < 0) {
|
||||
res = -errno;
|
||||
PRINT_ERROR("asprintf() failed: %d (%s)", res, strerror(-res));
|
||||
goto out;
|
||||
}
|
||||
env[6] = NULL;
|
||||
|
||||
PRINT_INFO("Invoking script %s with parameters: %s %s %s %s %s and environment: "
|
||||
"%s %s %s %s %s", stpg_path, args[1], args[2], args[3],
|
||||
args[4], args[5], env[1], env[2], env[3], env[4], env[5]);
|
||||
|
||||
c_pid = fork();
|
||||
if (c_pid == 0) {
|
||||
ret = setpgid(getpid(), getpid());
|
||||
if (ret < 0) {
|
||||
res = -errno;
|
||||
PRINT_ERROR("setgid failed %d (%s)", ret, strerror(-ret));
|
||||
}
|
||||
TRACE_DBG("pgid %d (pid %d)", getpgid(getpid()), getpid());
|
||||
ret = execve(stpg_path, args, env);
|
||||
if (ret < 0) {
|
||||
res = -errno;
|
||||
PRINT_ERROR("EXEC failed %d (%s)", ret, strerror(-ret));
|
||||
}
|
||||
exit(0);
|
||||
} else if (c_pid < 0) {
|
||||
res = -errno;
|
||||
PRINT_ERROR("fork() failed: %d (%s)", res, strerror(-res));
|
||||
}
|
||||
|
||||
*out_pid = c_pid;
|
||||
|
||||
for (i = 1; i < (signed)ARRAY_SIZE(env); i++)
|
||||
free(env[i]);
|
||||
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Returns 0, if the pid is still running, >0 if it was exited or <0 error code */
|
||||
int wait_until_finished(pid_t pid, unsigned long deadline, int *status, int child)
|
||||
{
|
||||
int res;
|
||||
time_t start, end;
|
||||
double elapsed;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
time(&start);
|
||||
do {
|
||||
res = waitpid(pid, status, WNOHANG);
|
||||
if (res != 0) {
|
||||
if (res < 0) {
|
||||
res = -errno;
|
||||
PRINT_ERROR("Waitpid for pid %d (child %d) "
|
||||
"failed: %d (%s)", pid, child,
|
||||
errno, strerror(errno));
|
||||
}
|
||||
break;
|
||||
}
|
||||
sleep(0.1);
|
||||
time(&end);
|
||||
elapsed = difftime(end, start);
|
||||
} while (elapsed < deadline);
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
int handle_stpg_received(struct scst_event_user *event_user)
|
||||
{
|
||||
const struct scst_event_stpg_payload *p = (struct scst_event_stpg_payload *)event_user->out_event.payload;
|
||||
int num, k;
|
||||
int res = 0;
|
||||
pid_t pids[p->stpg_descriptors_cnt];
|
||||
|
||||
TRACE_DBG("device name %s, stpg_descriptors_cnt %d", p->device_name,
|
||||
p->stpg_descriptors_cnt);
|
||||
|
||||
for (num = 0; num < p->stpg_descriptors_cnt; num++) {
|
||||
res = invoke_stpg(p->device_name, &p->stpg_descriptors[num], &pids[num]);
|
||||
TRACE_DBG("num %d, res %d, pid %d", num, res, pids[num]);
|
||||
if (res != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
TRACE_DBG("num %d", num);
|
||||
for (k = 0; k < num; k++) {
|
||||
int status = 0, rc;
|
||||
|
||||
TRACE_DBG("k %d, pid %d", k, pids[k]);
|
||||
|
||||
rc = wait_until_finished(pids[k], transition_timeout, &status, k);
|
||||
TRACE_DBG("rc %d, status %d", rc, WEXITSTATUS(status));
|
||||
if (rc > 0) {
|
||||
if (res == 0)
|
||||
res = WEXITSTATUS(status);
|
||||
continue;
|
||||
} else if (rc < 0) {
|
||||
if (res == 0)
|
||||
res = rc;
|
||||
continue;
|
||||
}
|
||||
|
||||
PRINT_WARNING("on_stpg %d (pid %d) did not finish on time - "
|
||||
"sending SIGTERM", k, pids[k]);
|
||||
if (res == 0)
|
||||
res = -ETIMEDOUT;
|
||||
rc = killpg(pids[k], SIGTERM);
|
||||
if (rc < 0)
|
||||
PRINT_ERROR("Failed to send SIGTERM to child %d (pid %d): %d/%s",
|
||||
k, pids[k], errno, strerror(errno));
|
||||
|
||||
rc = wait_until_finished(pids[k], 1, &status, k);
|
||||
if (rc != 0)
|
||||
continue;
|
||||
|
||||
while (1) {
|
||||
PRINT_WARNING("on_stpg %d (pid %d) did not finish on time - "
|
||||
"sending SIGKILL", k, pids[k]);
|
||||
rc = killpg(pids[k], SIGKILL);
|
||||
if (rc < 0) {
|
||||
PRINT_ERROR("Failed to send SIGKILL to child %d "
|
||||
"(pid %d): %d/%s", k, pids[k], errno,
|
||||
strerror(errno));
|
||||
break;
|
||||
}
|
||||
rc = wait_until_finished(pids[k], 1, &status, k);
|
||||
if (rc != 0)
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int stpg_event_loop(void)
|
||||
{
|
||||
int res = 0, status;
|
||||
int event_fd;
|
||||
uint8_t event_user_buf[1024*1024];
|
||||
pid_t c_pid = 0;
|
||||
struct pollfd pl;
|
||||
struct scst_event_user *event_user =
|
||||
(struct scst_event_user *)event_user_buf;
|
||||
struct scst_event e1;
|
||||
bool first_error = true;
|
||||
|
||||
event_fd = open(SCST_EVENT_DEV, O_RDWR);
|
||||
if (event_fd < 0) {
|
||||
res = -errno;
|
||||
PRINT_ERROR("Unable to open SCST event device %s (%s)",
|
||||
SCST_EVENT_DEV, strerror(-res));
|
||||
goto out;
|
||||
}
|
||||
|
||||
close(stpg_init_report_pipe[0]);
|
||||
|
||||
if (log_daemon)
|
||||
res = write(stpg_init_report_pipe[1], &res, sizeof(res));
|
||||
|
||||
close(stpg_init_report_pipe[1]);
|
||||
|
||||
memset(&pl, 0, sizeof(pl));
|
||||
pl.fd = event_fd;
|
||||
pl.events = POLLIN;
|
||||
|
||||
memset(&e1, 0, sizeof(e1));
|
||||
e1.event_code = SCST_EVENT_STPG_USER_INVOKE;
|
||||
strncpy(e1.issuer_name, SCST_EVENT_SCST_CORE_ISSUER,
|
||||
sizeof(e1.issuer_name));
|
||||
e1.issuer_name[sizeof(e1.issuer_name)-1] = '\0';
|
||||
PRINT_INFO("Setting allowed event code %d, issuer_name %s",
|
||||
e1.event_code, e1.issuer_name);
|
||||
|
||||
res = ioctl(event_fd, SCST_EVENT_ALLOW_EVENT, &e1);
|
||||
if (res != 0) {
|
||||
res = -errno;
|
||||
PRINT_ERROR("SCST_EVENT_ALLOW_EVENT failed: %d (%s)",
|
||||
res, strerror(-res));
|
||||
goto out;
|
||||
}
|
||||
|
||||
e1.event_code = SCST_EVENT_TM_FN_RECEIVED;
|
||||
strncpy(e1.issuer_name, SCST_EVENT_SCST_CORE_ISSUER,
|
||||
sizeof(e1.issuer_name));
|
||||
e1.issuer_name[sizeof(e1.issuer_name)-1] = '\0';
|
||||
PRINT_INFO("Setting allowed event code %d, issuer_name %s",
|
||||
e1.event_code, e1.issuer_name);
|
||||
res = ioctl(event_fd, SCST_EVENT_ALLOW_EVENT, &e1);
|
||||
if (res != 0) {
|
||||
res = -errno;
|
||||
PRINT_ERROR("SCST_EVENT_ALLOW_EVENT failed: %d (%s)",
|
||||
res, strerror(-res));
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
memset(event_user_buf, 0, sizeof(event_user_buf));
|
||||
event_user->max_event_size = sizeof(event_user_buf);
|
||||
res = ioctl(event_fd, SCST_EVENT_GET_NEXT_EVENT, event_user);
|
||||
if (res != 0) {
|
||||
res = -errno;
|
||||
switch (-res) {
|
||||
case ESRCH:
|
||||
case EBUSY:
|
||||
TRACE_MGMT_DBG("SCST_EVENT_GET_NEXT_EVENT "
|
||||
"returned %d (%s)", res, strerror(res));
|
||||
/* go through */
|
||||
case EINTR:
|
||||
continue;
|
||||
case EAGAIN:
|
||||
TRACE_DBG("SCST_EVENT_GET_NEXT_EVENT, "
|
||||
"returned EAGAIN (%d)", -res);
|
||||
continue;
|
||||
default:
|
||||
PRINT_ERROR("SCST_EVENT_GET_NEXT_EVENT "
|
||||
"failed: %d (%s)", res, strerror(-res));
|
||||
if (!first_error)
|
||||
goto out;
|
||||
first_error = false;
|
||||
continue;
|
||||
}
|
||||
first_error = true;
|
||||
again_poll:
|
||||
res = poll(&pl, 1, c_pid > 0 ? 1 : 0);
|
||||
if (res > 0)
|
||||
continue;
|
||||
else if (res == 0)
|
||||
goto again_poll;
|
||||
else {
|
||||
res = -errno;
|
||||
switch (res) {
|
||||
case ESRCH:
|
||||
case EBUSY:
|
||||
case EAGAIN:
|
||||
TRACE_MGMT_DBG("poll() returned %d "
|
||||
"(%s)", res, strerror(-res));
|
||||
case EINTR:
|
||||
goto again_poll;
|
||||
default:
|
||||
PRINT_ERROR("poll() failed: %d (%s)",
|
||||
res, strerror(-res));
|
||||
goto again_poll;
|
||||
}
|
||||
}
|
||||
}
|
||||
first_error = true;
|
||||
#ifdef DEBUG
|
||||
PRINT_INFO("event_code %d, issuer_name %s",
|
||||
event_user->out_event.event_code,
|
||||
event_user->out_event.issuer_name);
|
||||
#endif
|
||||
if (event_user->out_event.payload_len != 0)
|
||||
TRACE_BUFFER("payload", event_user->out_event.payload,
|
||||
event_user->out_event.payload_len);
|
||||
|
||||
if (event_user->out_event.event_code == SCST_EVENT_STPG_USER_INVOKE) {
|
||||
c_pid = fork();
|
||||
if (c_pid == -1)
|
||||
PRINT_ERROR("Failed to fork: %d", c_pid);
|
||||
else if (c_pid == 0) {
|
||||
struct scst_event_notify_done d;
|
||||
|
||||
signal(SIGCHLD, SIG_DFL);
|
||||
|
||||
status = handle_stpg_received(event_user);
|
||||
|
||||
memset(&d, 0, sizeof(d));
|
||||
d.event_id = event_user->out_event.event_id;
|
||||
d.status = status;
|
||||
res = ioctl(event_fd, SCST_EVENT_NOTIFY_DONE, &d);
|
||||
if (res != 0) {
|
||||
res = -errno;
|
||||
PRINT_ERROR("SCST_EVENT_NOTIFY_DONE "
|
||||
"failed: %s (res %d)",
|
||||
strerror(-res), res);
|
||||
} else
|
||||
PRINT_INFO("STPG event completed with status %d", status);
|
||||
exit(res);
|
||||
}
|
||||
} else if (event_user->out_event.event_code == SCST_EVENT_TM_FN_RECEIVED)
|
||||
stpg_handle_tm_received(event_user);
|
||||
else
|
||||
PRINT_ERROR("Unknown event %d received", event_user->out_event.event_code);
|
||||
}
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
|
||||
void sig_chld(int signal)
|
||||
{
|
||||
/* Check just in case */
|
||||
if (signal == SIGCHLD) {
|
||||
TRACE_DBG("Cleanup zombie (pid %d)", getpid());
|
||||
wait(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int res = 0, ch, longindex;
|
||||
pid_t pid;
|
||||
struct sigaction sa;
|
||||
|
||||
setlinebuf(stdout);
|
||||
|
||||
openlog(argv[0], LOG_PID, LOG_USER);
|
||||
|
||||
res = pipe(stpg_init_report_pipe);
|
||||
if (res == -1) {
|
||||
res = -errno;
|
||||
PRINT_ERROR("Pipe failed: %d (%s)", res, strerror(-res));
|
||||
goto out;
|
||||
}
|
||||
|
||||
sa.sa_handler = &sig_chld;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
|
||||
if (sigaction(SIGCHLD, &sa, 0) == -1) {
|
||||
PRINT_ERROR("sigaction() failed: %d/%s", errno, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Otherwise we could die in some later write() during the event_loop()
|
||||
* instead of getting EPIPE!
|
||||
*/
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
res = debug_init();
|
||||
if (res != 0)
|
||||
goto out;
|
||||
|
||||
app_name = argv[0];
|
||||
|
||||
while ((ch = getopt_long(argc, argv, "+d:fp:t:hv",
|
||||
long_options, &longindex)) >= 0) {
|
||||
switch (ch) {
|
||||
case 'p':
|
||||
stpg_path = optarg;
|
||||
break;
|
||||
#if defined(DEBUG) || defined(TRACING)
|
||||
case 'd':
|
||||
trace_flag = strtol(optarg, (char **)NULL, 0);
|
||||
break;
|
||||
#endif
|
||||
case 'v':
|
||||
printf("%s version %s\n", app_name, VERSION_STR);
|
||||
goto out_done;
|
||||
case 'f':
|
||||
log_daemon = false;
|
||||
break;
|
||||
case 't':
|
||||
transition_timeout = strtol(optarg, (char **)NULL, 0);
|
||||
if (transition_timeout < 0) {
|
||||
printf("Invalid timeout %d\n", transition_timeout);
|
||||
res = -EINVAL;
|
||||
goto out_done;
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
usage(0);
|
||||
goto out_done;
|
||||
default:
|
||||
goto out_usage;
|
||||
}
|
||||
}
|
||||
|
||||
if (!stpg_path)
|
||||
stpg_path = "/usr/local/bin/scst/scst_on_stpg";
|
||||
|
||||
if (access(stpg_path, X_OK) == -1) {
|
||||
PRINT_ERROR("Script file \" %s \"does not exist or not "
|
||||
"executable", stpg_path);
|
||||
res = -1;
|
||||
goto out_done;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
PRINT_INFO("trace_flag %lx", trace_flag);
|
||||
#endif
|
||||
if (log_daemon) {
|
||||
trace_flag &= ~TRACE_TIME;
|
||||
trace_flag &= ~TRACE_PID;
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
PRINT_ERROR("starting daemon failed(%d)", pid);
|
||||
res = pid;
|
||||
goto out_done;
|
||||
} else if (pid) {
|
||||
int res1 = -1;
|
||||
|
||||
close(stpg_init_report_pipe[1]);
|
||||
if ((unsigned)read(stpg_init_report_pipe[0], &res1, sizeof(res1)) < sizeof(res1)) {
|
||||
res = -1;
|
||||
goto out_done;
|
||||
} else {
|
||||
res = res1;
|
||||
goto out_done;
|
||||
}
|
||||
}
|
||||
|
||||
close(0);
|
||||
open("/dev/null", O_RDWR);
|
||||
dup2(0, 1);
|
||||
dup2(0, 2);
|
||||
setsid();
|
||||
}
|
||||
|
||||
res = stpg_event_loop();
|
||||
|
||||
out_done:
|
||||
debug_done();
|
||||
|
||||
out:
|
||||
closelog();
|
||||
return res;
|
||||
|
||||
out_usage:
|
||||
usage(1);
|
||||
goto out_done;
|
||||
}
|
||||
Reference in New Issue
Block a user