mirror of
https://github.com/SCST-project/scst.git
synced 2026-05-28 09:20:18 +00:00
A huge chunk of related to each other changes, which had to be tested together.
iSCSI-SCST: A huge improvements in errors recovery and iSCSI RFC complaince as well as performance. Also: - Fixes and improvements for MaxOutstandingR2T>1 - Flow control tracing added. - Cleanups SCST core: - Now for scst_cmd_init_stage1_done() commands preprocessing_done() is always called before xmit_response(), even in case of abort or error. - Fixed recently introduced bug, which can lead to sending responses for aborted commands after reply on the corresponding TM command already sent. - Flow control tracing added. - Now it is possible to call functions setting commands execution status (e.g., scst_set_cmd_error_status()) several times for the same command. Only the first call will be completed, other calls - ignored. - All commands are counted and shown in proc/sysfs now. Before only active, i.e. not yet executed commands, were counted and shown there. git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@1431 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
- Fix support of ranges in parameters negotiation.
|
||||
|
||||
- Fix SNACK command handling. Currently it violates iSCSI RFC.
|
||||
|
||||
- Minor "ToDo"'s spread in the code.
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#define ISCSI_PROC_LOG_ENTRY_NAME "trace_level"
|
||||
|
||||
static struct scst_trace_log iscsi_local_trace_tbl[] = {
|
||||
{ TRACE_D_READ, "d_read" },
|
||||
{ TRACE_D_WRITE, "d_write" },
|
||||
{ TRACE_CONN_OC, "conn" },
|
||||
{ TRACE_CONN_OC_DBG, "conn_dbg" },
|
||||
@@ -605,7 +604,7 @@ void iscsi_dump_pdu(struct iscsi_pdu *pdu)
|
||||
|
||||
buf = (void *)&pdu->bhs;
|
||||
printk(KERN_DEBUG "BHS: (%p,%zd)\n", buf, sizeof(pdu->bhs));
|
||||
for (i = 0; i < sizeof(pdu->bhs); i++)
|
||||
for (i = 0; i < (int)sizeof(pdu->bhs); i++)
|
||||
iscsi_dump_char(*buf++, text, &pos);
|
||||
iscsi_dump_char(-1, text, &pos);
|
||||
|
||||
@@ -618,4 +617,25 @@ void iscsi_dump_pdu(struct iscsi_pdu *pdu)
|
||||
printk(KERN_DEBUG "Data: (%d)\n", pdu->datasize);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long iscsi_get_flow_ctrl_or_mgmt_dbg_log_flag(struct iscsi_cmnd *cmnd)
|
||||
{
|
||||
unsigned long flag;
|
||||
|
||||
if (cmnd->cmd_req != NULL)
|
||||
cmnd = cmnd->cmd_req;
|
||||
|
||||
if (cmnd->scst_cmd == NULL)
|
||||
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;
|
||||
else
|
||||
flag = TRACE_MGMT_DEBUG;
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SCST_DEBUG */
|
||||
|
||||
@@ -372,6 +372,9 @@ static void iscsi_write_space_ready(struct sock *sk)
|
||||
static void conn_rsp_timer_fn(unsigned long arg)
|
||||
{
|
||||
struct iscsi_conn *conn = (struct iscsi_conn *)arg;
|
||||
struct iscsi_cmnd *cmnd;
|
||||
unsigned long j = jiffies;
|
||||
unsigned long timeout_time = j + ISCSI_RSP_SCHED_TIMEOUT;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
@@ -379,32 +382,107 @@ static void conn_rsp_timer_fn(unsigned long arg)
|
||||
|
||||
spin_lock_bh(&conn->write_list_lock);
|
||||
|
||||
if (!list_empty(&conn->written_list)) {
|
||||
struct iscsi_cmnd *wr_cmd = list_entry(conn->written_list.next,
|
||||
struct iscsi_cmnd, written_list_entry);
|
||||
if (!list_empty(&conn->write_timeout_list)) {
|
||||
cmnd = list_entry(conn->write_timeout_list.next,
|
||||
struct iscsi_cmnd, write_timeout_list_entry);
|
||||
|
||||
if (unlikely(time_after_eq(jiffies, wr_cmd->write_timeout))) {
|
||||
if (unlikely(time_after_eq(j,
|
||||
cmnd->write_start + ISCSI_RSP_TIMEOUT))) {
|
||||
if (!conn->closing) {
|
||||
PRINT_ERROR("Timeout sending data to initiator"
|
||||
" %s (SID %llx), closing connection",
|
||||
PRINT_ERROR("Timeout sending data/waiting "
|
||||
"for reply to/from initiator "
|
||||
"%s (SID %llx), closing connection",
|
||||
conn->session->initiator_name,
|
||||
(long long unsigned int)
|
||||
conn->session->sid);
|
||||
/*
|
||||
* We must call mark_conn_closed() outside of
|
||||
* write_list_lock or we will have a circular
|
||||
* locking dependency with iscsi_rd_lock.
|
||||
*/
|
||||
spin_unlock_bh(&conn->write_list_lock);
|
||||
mark_conn_closed(conn);
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
} else if (!timer_pending(&conn->rsp_timer) ||
|
||||
time_after(conn->rsp_timer.expires, timeout_time)) {
|
||||
TRACE_DBG("Restarting timer on %ld (conn %p)",
|
||||
wr_cmd->write_timeout, conn);
|
||||
timeout_time, conn);
|
||||
/*
|
||||
* Timer might have been restarted while we were
|
||||
* entering here.
|
||||
*/
|
||||
mod_timer(&conn->rsp_timer, wr_cmd->write_timeout);
|
||||
mod_timer(&conn->rsp_timer, timeout_time);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_bh(&conn->write_list_lock);
|
||||
|
||||
if (unlikely(conn->conn_tm_active)) {
|
||||
TRACE_MGMT_DBG("TM active: making conn %p RD active", conn);
|
||||
iscsi_make_conn_rd_active(conn);
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
void iscsi_check_tm_data_wait_timeouts(struct iscsi_conn *conn, bool force)
|
||||
{
|
||||
struct iscsi_cmnd *cmnd;
|
||||
unsigned long j = jiffies;
|
||||
bool aborted_cmds_pending;
|
||||
unsigned long timeout_time = j + ISCSI_TM_DATA_WAIT_SCHED_TIMEOUT;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_DBG_FLAG(force ? TRACE_CONN_OC_DBG : TRACE_MGMT_DEBUG,
|
||||
"j %ld (TIMEOUT %d, force %d)", j,
|
||||
ISCSI_TM_DATA_WAIT_SCHED_TIMEOUT, force);
|
||||
|
||||
again:
|
||||
spin_lock_bh(&iscsi_rd_lock);
|
||||
spin_lock(&conn->write_list_lock);
|
||||
|
||||
aborted_cmds_pending = false;
|
||||
list_for_each_entry(cmnd, &conn->write_timeout_list,
|
||||
write_timeout_list_entry) {
|
||||
if (test_bit(ISCSI_CMD_ABORTED, &cmnd->prelim_compl_flags)) {
|
||||
TRACE_DBG_FLAG(force ? TRACE_CONN_OC_DBG : TRACE_MGMT_DEBUG,
|
||||
"Checking aborted cmnd %p (scst_state %d, "
|
||||
"on_write_timeout_list %d, write_start %ld, "
|
||||
"r2t_len_to_receive %d)", cmnd,
|
||||
cmnd->scst_state, cmnd->on_write_timeout_list,
|
||||
cmnd->write_start, cmnd->r2t_len_to_receive);
|
||||
if ((cmnd->r2t_len_to_receive != 0) &&
|
||||
(time_after_eq(j, cmnd->write_start + ISCSI_TM_DATA_WAIT_TIMEOUT) ||
|
||||
force)) {
|
||||
spin_unlock(&conn->write_list_lock);
|
||||
spin_unlock_bh(&iscsi_rd_lock);
|
||||
iscsi_fail_data_waiting_cmnd(cmnd);
|
||||
goto again;
|
||||
}
|
||||
aborted_cmds_pending = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (aborted_cmds_pending) {
|
||||
if (!force &&
|
||||
(!timer_pending(&conn->rsp_timer) ||
|
||||
time_after(conn->rsp_timer.expires, timeout_time))) {
|
||||
TRACE_MGMT_DBG("Mod timer on %ld (conn %p)",
|
||||
timeout_time, conn);
|
||||
mod_timer(&conn->rsp_timer, timeout_time);
|
||||
}
|
||||
} else {
|
||||
TRACE_MGMT_DBG("Clearing conn_tm_active for conn %p", conn);
|
||||
conn->conn_tm_active = 0;
|
||||
}
|
||||
|
||||
spin_unlock(&conn->write_list_lock);
|
||||
spin_unlock_bh(&iscsi_rd_lock);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
@@ -515,7 +593,7 @@ static int conn_free(struct iscsi_conn *conn)
|
||||
sBUG_ON(atomic_read(&conn->conn_ref_cnt) != 0);
|
||||
sBUG_ON(!list_empty(&conn->cmd_list));
|
||||
sBUG_ON(!list_empty(&conn->write_list));
|
||||
sBUG_ON(!list_empty(&conn->written_list));
|
||||
sBUG_ON(!list_empty(&conn->write_timeout_list));
|
||||
sBUG_ON(conn->conn_reinst_successor != NULL);
|
||||
sBUG_ON(!test_bit(ISCSI_CONN_SHUTTINGDOWN, &conn->conn_aflags));
|
||||
|
||||
@@ -599,7 +677,7 @@ static int iscsi_conn_alloc(struct iscsi_session *session,
|
||||
INIT_LIST_HEAD(&conn->cmd_list);
|
||||
spin_lock_init(&conn->write_list_lock);
|
||||
INIT_LIST_HEAD(&conn->write_list);
|
||||
INIT_LIST_HEAD(&conn->written_list);
|
||||
INIT_LIST_HEAD(&conn->write_timeout_list);
|
||||
setup_timer(&conn->rsp_timer, conn_rsp_timer_fn, (unsigned long)conn);
|
||||
init_waitqueue_head(&conn->read_state_waitQ);
|
||||
init_completion(&conn->ready_to_free);
|
||||
|
||||
@@ -157,12 +157,14 @@ int digest_rx_data(struct iscsi_cmnd *cmnd)
|
||||
u32 offset, crc;
|
||||
int res = 0;
|
||||
|
||||
if (unlikely(cmnd->rejected))
|
||||
goto out;
|
||||
|
||||
switch (cmnd_opcode(cmnd)) {
|
||||
case ISCSI_OP_SCSI_DATA_OUT:
|
||||
req = cmnd->cmd_req;
|
||||
if (unlikely(req == NULL)) {
|
||||
/* It can be for prelim completed commands */
|
||||
req = cmnd;
|
||||
goto out;
|
||||
}
|
||||
req_hdr = (struct iscsi_data_out_hdr *)&cmnd->pdu.bhs;
|
||||
offset = be32_to_cpu(req_hdr->buffer_offset);
|
||||
break;
|
||||
@@ -172,6 +174,16 @@ int digest_rx_data(struct iscsi_cmnd *cmnd)
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to skip the digest check for prelim completed commands,
|
||||
* because we use shared data buffer for them, so, most likely, the
|
||||
* check will fail. Plus, for such commands we sometimes don't have
|
||||
* sg_cnt set correctly (cmnd_prepare_get_rejected_cmd_data() doesn't
|
||||
* do it).
|
||||
*/
|
||||
if (unlikely(req->prelim_compl_flags != 0))
|
||||
goto out;
|
||||
|
||||
crc = digest_data(req, cmnd->pdu.datasize, offset,
|
||||
cmnd->conn->rpadding);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -29,8 +29,9 @@
|
||||
|
||||
#include "iscsi_dbg.h"
|
||||
|
||||
#define iscsi_sense_crc_error ABORTED_COMMAND, 0x47, 0x5
|
||||
#define iscsi_sense_unexpected_unsolicited_data ABORTED_COMMAND, 0xC, 0xC
|
||||
#define iscsi_sense_crc_error ABORTED_COMMAND, 0x47, 0x05
|
||||
#define iscsi_sense_unexpected_unsolicited_data ABORTED_COMMAND, 0x0C, 0x0C
|
||||
#define iscsi_sense_incorrect_amount_of_data ABORTED_COMMAND, 0x0C, 0x0D
|
||||
|
||||
struct iscsi_sess_param {
|
||||
int initial_r2t;
|
||||
@@ -120,8 +121,13 @@ struct iscsi_session {
|
||||
/* Read only, if there are connection(s) */
|
||||
struct iscsi_sess_param sess_param;
|
||||
|
||||
spinlock_t cmnd_hash_lock;
|
||||
struct list_head cmnd_hash[1 << ISCSI_HASH_ORDER];
|
||||
/*
|
||||
* In some corner cases commands can be deleted from the hash
|
||||
* not from the corresponding read thread. So, let's simplify
|
||||
* errors recovery and have this lock.
|
||||
*/
|
||||
spinlock_t cmnd_data_wait_hash_lock;
|
||||
struct list_head cmnd_data_wait_hash[1 << ISCSI_HASH_ORDER];
|
||||
|
||||
struct list_head conn_list; /* protected by target_mutex */
|
||||
|
||||
@@ -170,7 +176,7 @@ struct iscsi_conn {
|
||||
/* List of data pdus to be sent, protected by write_list_lock */
|
||||
struct list_head write_list;
|
||||
/* List of data pdus being sent, protected by write_list_lock */
|
||||
struct list_head written_list;
|
||||
struct list_head write_timeout_list;
|
||||
|
||||
struct timer_list rsp_timer;
|
||||
|
||||
@@ -208,13 +214,14 @@ struct iscsi_conn {
|
||||
int hdigest_type;
|
||||
int ddigest_type;
|
||||
|
||||
/* All 5 protected by iscsi_rd_lock */
|
||||
/* All 6 protected by iscsi_rd_lock */
|
||||
unsigned short rd_state;
|
||||
unsigned short rd_data_ready:1;
|
||||
/* Let's save some cache footprint by putting them here */
|
||||
unsigned short closing:1;
|
||||
unsigned short active_close:1;
|
||||
unsigned short deleting:1;
|
||||
unsigned short conn_tm_active:1;
|
||||
|
||||
struct list_head rd_list_entry;
|
||||
|
||||
@@ -264,37 +271,38 @@ struct iscsi_pdu {
|
||||
typedef void (iscsi_show_info_t)(struct seq_file *seq,
|
||||
struct iscsi_target *target);
|
||||
|
||||
/** Command's states **/
|
||||
/** Commands' states **/
|
||||
|
||||
/* New command and SCST processes it */
|
||||
#define ISCSI_CMD_STATE_NEW 0
|
||||
#define ISCSI_CMD_STATE_NEW 0
|
||||
|
||||
/* SCST processes cmd after scst_rx_cmd() */
|
||||
#define ISCSI_CMD_STATE_RX_CMD 1
|
||||
#define ISCSI_CMD_STATE_RX_CMD 1
|
||||
|
||||
/* The command returned from preprocessing_done() */
|
||||
#define ISCSI_CMD_STATE_AFTER_PREPROC 2
|
||||
#define ISCSI_CMD_STATE_AFTER_PREPROC 2
|
||||
|
||||
/* The command is waiting for session or connection reinstatement finished */
|
||||
#define ISCSI_CMD_STATE_REINST_PENDING 3
|
||||
#define ISCSI_CMD_STATE_REINST_PENDING 3
|
||||
|
||||
/* scst_restart_cmd() called and SCST processing it */
|
||||
#define ISCSI_CMD_STATE_RESTARTED 4
|
||||
#define ISCSI_CMD_STATE_RESTARTED 4
|
||||
|
||||
/* SCST done processing */
|
||||
#define ISCSI_CMD_STATE_PROCESSED 5
|
||||
#define ISCSI_CMD_STATE_PROCESSED 5
|
||||
|
||||
/* AEN processing */
|
||||
#define ISCSI_CMD_STATE_AEN 6
|
||||
#define ISCSI_CMD_STATE_AEN 6
|
||||
|
||||
/** Command's reject reasons **/
|
||||
#define ISCSI_REJECT_SCSI_CMD 1
|
||||
#define ISCSI_REJECT_CMD 2
|
||||
#define ISCSI_REJECT_DATA 3
|
||||
/* Out of SCST core preliminary completed */
|
||||
#define ISCSI_CMD_STATE_OUT_OF_SCST_PRELIM_COMPL 7
|
||||
|
||||
/*
|
||||
* Most of the fields don't need any protection, since accessed from only a
|
||||
* single thread, except where noted.
|
||||
*
|
||||
* ToDo: Eventually divide request and response structures in 2 separate
|
||||
* structures and stop this IET-derived garbage.
|
||||
*/
|
||||
struct iscsi_cmnd {
|
||||
struct iscsi_conn *conn;
|
||||
@@ -310,31 +318,41 @@ struct iscsi_cmnd {
|
||||
unsigned int own_sg:1;
|
||||
unsigned int on_write_list:1;
|
||||
unsigned int write_processing_started:1;
|
||||
unsigned int data_waiting:1;
|
||||
unsigned int force_cleanup_done:1;
|
||||
unsigned int dec_active_cmnds:1;
|
||||
unsigned int ddigest_checked:1;
|
||||
unsigned int rejected:1;
|
||||
unsigned int reject_reason:2;
|
||||
#ifdef CONFIG_SCST_EXTRACHECKS
|
||||
unsigned int on_rx_digest_list:1;
|
||||
unsigned int release_called:1;
|
||||
#endif
|
||||
|
||||
/* It's async. with the above flags */
|
||||
volatile unsigned int tm_aborted;
|
||||
/*
|
||||
* We suppose that preliminary commands completion is tested by
|
||||
* comparing prelim_compl_flags with 0. Otherwise, because of the
|
||||
* gap between setting different flags a race is possible,
|
||||
* like sending command in SCST core as PRELIM_COMPLETED, while it
|
||||
* wasn't aborted in it yet and have as the result a wrong success
|
||||
* status sent to the initiator.
|
||||
*/
|
||||
#define ISCSI_CMD_ABORTED 0
|
||||
#define ISCSI_CMD_PRELIM_COMPLETED 1
|
||||
unsigned long prelim_compl_flags;
|
||||
|
||||
struct list_head hash_list_entry;
|
||||
|
||||
spinlock_t rsp_cmd_lock; /* BH lock */
|
||||
|
||||
/*
|
||||
* Unions are for readability and grepability and to save some
|
||||
* cache footprint.
|
||||
*/
|
||||
|
||||
union {
|
||||
/* Protected by rsp_cmd_lock */
|
||||
/*
|
||||
* Used only to abort not yet sent responses. Usage in
|
||||
* cmnd_done() is only a side effect to have a lockless
|
||||
* accesss to this list from always only a single thread
|
||||
* at any time. So, all responses live in the parent
|
||||
* until it has the last reference put.
|
||||
*/
|
||||
struct list_head rsp_cmd_list;
|
||||
struct list_head rsp_cmd_list_entry;
|
||||
};
|
||||
@@ -346,12 +364,12 @@ struct iscsi_cmnd {
|
||||
|
||||
union {
|
||||
struct list_head write_list_entry;
|
||||
struct list_head written_list_entry;
|
||||
struct list_head write_timeout_list_entry;
|
||||
};
|
||||
|
||||
/* Both modified only from single write thread */
|
||||
unsigned int on_written_list:1;
|
||||
unsigned long write_timeout;
|
||||
unsigned int on_write_timeout_list:1;
|
||||
unsigned long write_start;
|
||||
|
||||
/*
|
||||
* All unprotected, since could be accessed from only a single
|
||||
@@ -375,7 +393,9 @@ struct iscsi_cmnd {
|
||||
struct scst_cmd *scst_cmd;
|
||||
struct scst_aen *scst_aen;
|
||||
};
|
||||
int read_size;
|
||||
unsigned int read_size;
|
||||
|
||||
struct iscsi_cmnd *main_rsp;
|
||||
};
|
||||
|
||||
/* Response only fields */
|
||||
@@ -396,21 +416,29 @@ struct iscsi_cmnd {
|
||||
int sg_cnt;
|
||||
unsigned int bufflen;
|
||||
u32 r2t_sn;
|
||||
u32 r2t_length;
|
||||
u32 is_unsolicited_data;
|
||||
unsigned int r2t_len_to_receive;
|
||||
unsigned int r2t_len_to_send;
|
||||
unsigned int outstanding_r2t;
|
||||
u32 target_task_tag;
|
||||
u32 outstanding_r2t;
|
||||
|
||||
u32 hdigest;
|
||||
u32 ddigest;
|
||||
|
||||
struct list_head cmd_list_entry;
|
||||
};
|
||||
|
||||
/* Flags for req_cmnd_release_force() */
|
||||
#define ISCSI_FORCE_RELEASE_WRITE 1
|
||||
/**
|
||||
** Various timeouts. *_SCHED_TIMEOUT is needed to complete a burst of
|
||||
** commands at once. Otherwise, a part of the burst can be timeouted
|
||||
** only in double timeout time.
|
||||
**/
|
||||
|
||||
/* Max time to wait for our response satisfied */
|
||||
#define ISCSI_RSP_TIMEOUT (30 * HZ)
|
||||
#define ISCSI_RSP_SCHED_TIMEOUT (ISCSI_RSP_TIMEOUT + HZ)
|
||||
|
||||
/* Max time to wait for our response satisfied for aborted commands */
|
||||
#define ISCSI_TM_DATA_WAIT_TIMEOUT (10 * HZ)
|
||||
#define ISCSI_TM_DATA_WAIT_SCHED_TIMEOUT (ISCSI_TM_DATA_WAIT_TIMEOUT + HZ)
|
||||
|
||||
extern struct mutex target_mgmt_mutex;
|
||||
|
||||
@@ -432,11 +460,12 @@ extern int cmnd_rx_continue(struct iscsi_cmnd *req);
|
||||
extern void cmnd_rx_end(struct iscsi_cmnd *);
|
||||
extern void cmnd_tx_start(struct iscsi_cmnd *);
|
||||
extern void cmnd_tx_end(struct iscsi_cmnd *);
|
||||
extern void req_cmnd_release_force(struct iscsi_cmnd *req, int flags);
|
||||
extern void req_cmnd_release_force(struct iscsi_cmnd *req);
|
||||
extern void rsp_cmnd_release(struct iscsi_cmnd *);
|
||||
extern void cmnd_done(struct iscsi_cmnd *cmnd);
|
||||
extern void conn_abort(struct iscsi_conn *conn);
|
||||
extern void iscsi_restart_cmnd(struct iscsi_cmnd *cmnd);
|
||||
extern void iscsi_fail_data_waiting_cmnd(struct iscsi_cmnd *cmnd);
|
||||
|
||||
/* conn.c */
|
||||
extern struct iscsi_conn *conn_lookup(struct iscsi_session *, u16);
|
||||
@@ -447,17 +476,16 @@ extern int conn_del(struct iscsi_session *, struct iscsi_kern_conn_info *);
|
||||
extern int conn_free(struct iscsi_conn *);
|
||||
#endif
|
||||
extern void iscsi_make_conn_rd_active(struct iscsi_conn *conn);
|
||||
|
||||
#define ISCSI_CONN_ACTIVE_CLOSE 1
|
||||
#define ISCSI_CONN_DELETING 2
|
||||
extern void __mark_conn_closed(struct iscsi_conn *, int);
|
||||
|
||||
extern void mark_conn_closed(struct iscsi_conn *);
|
||||
extern void iscsi_make_conn_wr_active(struct iscsi_conn *);
|
||||
|
||||
#ifdef CONFIG_SCST_PROC
|
||||
extern void conn_info_show(struct seq_file *, struct iscsi_session *);
|
||||
#endif
|
||||
extern void iscsi_check_tm_data_wait_timeouts(struct iscsi_conn *conn,
|
||||
bool force);
|
||||
|
||||
/* nthread.c */
|
||||
extern int iscsi_send(struct iscsi_conn *conn);
|
||||
@@ -468,6 +496,7 @@ extern void iscsi_put_page_callback(struct page *page);
|
||||
extern int istrd(void *arg);
|
||||
extern int istwr(void *arg);
|
||||
extern void iscsi_task_mgmt_affected_cmds_done(struct scst_mgmt_cmd *scst_mcmd);
|
||||
extern void req_add_to_write_timeout_list(struct iscsi_cmnd *req);
|
||||
|
||||
/* target.c */
|
||||
#ifndef CONFIG_SCST_PROC
|
||||
@@ -598,7 +627,10 @@ static inline void cmnd_put(struct iscsi_cmnd *cmnd)
|
||||
static inline void cmd_add_on_write_list(struct iscsi_conn *conn,
|
||||
struct iscsi_cmnd *cmnd)
|
||||
{
|
||||
TRACE_DBG("%p", cmnd);
|
||||
TRACE_DBG("cmnd %p", cmnd);
|
||||
/* See comment in iscsi_restart_cmnd() */
|
||||
EXTRACHECKS_BUG_ON(cmnd->parent_req->hashed &&
|
||||
(cmnd_opcode(cmnd) != ISCSI_OP_R2T));
|
||||
list_add_tail(&cmnd->write_list_entry, &conn->write_list);
|
||||
cmnd->on_write_list = 1;
|
||||
}
|
||||
|
||||
@@ -20,18 +20,12 @@
|
||||
|
||||
#include <scst_debug.h>
|
||||
|
||||
#define TRACE_D_READ 0x80000000
|
||||
#define TRACE_D_WRITE 0x40000000
|
||||
#define TRACE_CONN_OC 0x20000000
|
||||
#define TRACE_D_IOV 0x10000000
|
||||
#define TRACE_D_DUMP_PDU 0x08000000
|
||||
#define TRACE_NET_PG 0x04000000
|
||||
#define TRACE_CONN_OC_DBG 0x02000000
|
||||
|
||||
#define TRACE_D_DATA (TRACE_D_READ | TRACE_D_WRITE)
|
||||
|
||||
#define TRACE_ALL_NO_DATA \
|
||||
(TRACE_ALL & ~TRACE_D_IOV & ~TRACE_D_DUMP_PDU & ~TRACE_D_DATA)
|
||||
#define TRACE_D_WRITE 0x80000000
|
||||
#define TRACE_CONN_OC 0x40000000
|
||||
#define TRACE_D_IOV 0x20000000
|
||||
#define TRACE_D_DUMP_PDU 0x10000000
|
||||
#define TRACE_NET_PG 0x08000000
|
||||
#define TRACE_CONN_OC_DBG 0x04000000
|
||||
|
||||
#ifdef CONFIG_SCST_DEBUG
|
||||
#define ISCSI_DEFAULT_LOG_FLAGS (TRACE_FUNCTION | TRACE_LINE | TRACE_PID | \
|
||||
@@ -44,9 +38,13 @@
|
||||
|
||||
#ifdef CONFIG_SCST_DEBUG
|
||||
struct iscsi_pdu;
|
||||
struct iscsi_cmnd;
|
||||
extern void iscsi_dump_pdu(struct iscsi_pdu *pdu);
|
||||
extern unsigned long iscsi_get_flow_ctrl_or_mgmt_dbg_log_flag(
|
||||
struct iscsi_cmnd *cmnd);
|
||||
#else
|
||||
#define iscsi_dump_pdu(x) do {} while (0)
|
||||
#define iscsi_get_flow_ctrl_or_mgmt_dbg_log_flag(x) do {} while (0)
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
|
||||
@@ -54,20 +52,9 @@ extern unsigned long iscsi_trace_flag;
|
||||
#define trace_flag iscsi_trace_flag
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SCST_DEBUG
|
||||
|
||||
#define TRACE_CONN_CLOSE(args...) TRACE(TRACE_CONN_OC, args)
|
||||
#define TRACE_CONN_CLOSE(args...) TRACE_DBG_FLAG(TRACE_DEBUG|TRACE_CONN_OC, args)
|
||||
#define TRACE_CONN_CLOSE_DBG(args...) TRACE(TRACE_CONN_OC_DBG, args)
|
||||
#define TRACE_NET_PAGE(args...) TRACE(TRACE_NET_PG, args)
|
||||
#define TRACE_WRITE(args...) TRACE(TRACE_D_WRITE, args)
|
||||
#define TRACE_READ(args...) TRACE(TRACE_D_READ, args)
|
||||
|
||||
#else /* CONFIG_SCST_DEBUG */
|
||||
#define TRACE_CONN_CLOSE(format, args...) {}
|
||||
#define TRACE_CONN_CLOSE_DBG(format, args...) {}
|
||||
#define TRACE_NET_PAGE(format, args...) {}
|
||||
#define TRACE_WRITE(args...) {}
|
||||
#define TRACE_READ(args...) {}
|
||||
#endif
|
||||
#define TRACE_NET_PAGE(args...) TRACE_DBG_FLAG(TRACE_NET_PG, args)
|
||||
#define TRACE_WRITE(args...) TRACE_DBG_FLAG(TRACE_DEBUG|TRACE_D_WRITE, args)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -511,7 +511,6 @@ struct iscsi_nop_in_hdr {
|
||||
#define ISCSI_RESERVED_TAG (0xffffffffU)
|
||||
|
||||
#define cmnd_hdr(cmnd) ((struct iscsi_scsi_cmd_hdr *) (&((cmnd)->pdu.bhs)))
|
||||
#define cmnd_ttt(cmnd) cpu_to_be32((cmnd)->pdu.bhs.ttt)
|
||||
#define cmnd_itt(cmnd) cpu_to_be32((cmnd)->pdu.bhs.itt)
|
||||
#define cmnd_opcode(cmnd) ((cmnd)->pdu.bhs.opcode & ISCSI_OPCODE_MASK)
|
||||
#define cmnd_scsicode(cmnd) (cmnd_hdr((cmnd))->scb[0])
|
||||
|
||||
@@ -78,11 +78,12 @@ again:
|
||||
struct iscsi_cmnd *rsp;
|
||||
int restart = 0;
|
||||
|
||||
TRACE_CONN_CLOSE_DBG("cmd %p, scst_state %x, data_waiting %d, "
|
||||
"ref_cnt %d, parent_req %p, net_ref_cnt %d, sg %p",
|
||||
cmnd, cmnd->scst_state, cmnd->data_waiting,
|
||||
atomic_read(&cmnd->ref_cnt), cmnd->parent_req,
|
||||
atomic_read(&cmnd->net_ref_cnt), cmnd->sg);
|
||||
TRACE_CONN_CLOSE_DBG("cmd %p, scst_state %x, "
|
||||
"r2t_len_to_receive %d, ref_cnt %d, parent_req %p, "
|
||||
"net_ref_cnt %d, sg %p", cmnd, cmnd->scst_state,
|
||||
cmnd->r2t_len_to_receive, atomic_read(&cmnd->ref_cnt),
|
||||
cmnd->parent_req, atomic_read(&cmnd->net_ref_cnt),
|
||||
cmnd->sg);
|
||||
|
||||
sBUG_ON(cmnd->parent_req != NULL);
|
||||
|
||||
@@ -113,7 +114,6 @@ again:
|
||||
goto again;
|
||||
}
|
||||
|
||||
spin_lock_bh(&cmnd->rsp_cmd_lock);
|
||||
list_for_each_entry(rsp, &cmnd->rsp_cmd_list,
|
||||
rsp_cmd_list_entry) {
|
||||
TRACE_CONN_CLOSE_DBG(" rsp %p, ref_cnt %d, "
|
||||
@@ -138,7 +138,6 @@ again:
|
||||
|
||||
if (page->net_priv != NULL) {
|
||||
if (restart == 0) {
|
||||
spin_unlock_bh(&cmnd->rsp_cmd_lock);
|
||||
spin_unlock_bh(&conn->cmd_list_lock);
|
||||
restart = 1;
|
||||
}
|
||||
@@ -152,7 +151,6 @@ again:
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&cmnd->rsp_cmd_lock);
|
||||
}
|
||||
spin_unlock_bh(&conn->cmd_list_lock);
|
||||
|
||||
@@ -191,7 +189,7 @@ static void free_pending_commands(struct iscsi_conn *conn)
|
||||
|
||||
spin_unlock(&session->sn_lock);
|
||||
|
||||
req_cmnd_release_force(cmnd, 0);
|
||||
req_cmnd_release_force(cmnd);
|
||||
|
||||
req_freed = 1;
|
||||
spin_lock(&session->sn_lock);
|
||||
@@ -231,7 +229,7 @@ static void free_orphaned_pending_commands(struct iscsi_conn *conn)
|
||||
|
||||
spin_unlock(&session->sn_lock);
|
||||
|
||||
req_cmnd_release_force(cmnd, 0);
|
||||
req_cmnd_release_force(cmnd);
|
||||
|
||||
req_freed = 1;
|
||||
spin_lock(&session->sn_lock);
|
||||
@@ -261,13 +259,13 @@ static void trace_conn_close(struct iscsi_conn *conn)
|
||||
list_for_each_entry(cmnd, &conn->cmd_list,
|
||||
cmd_list_entry) {
|
||||
TRACE_CONN_CLOSE_DBG(
|
||||
"cmd %p, scst_state %x, scst_cmd state %d, "
|
||||
"data_waiting %d, ref_cnt %d, sn %u, "
|
||||
"cmd %p, scst_cmd %p, scst_state %x, scst_cmd state "
|
||||
"%d, r2t_len_to_receive %d, ref_cnt %d, sn %u, "
|
||||
"parent_req %p, pending %d",
|
||||
cmnd, cmnd->scst_state,
|
||||
(cmnd->parent_req && cmnd->scst_cmd) ?
|
||||
cmnd, cmnd->scst_cmd, cmnd->scst_state,
|
||||
((cmnd->parent_req == NULL) && cmnd->scst_cmd) ?
|
||||
cmnd->scst_cmd->state : -1,
|
||||
cmnd->data_waiting, atomic_read(&cmnd->ref_cnt),
|
||||
cmnd->r2t_len_to_receive, atomic_read(&cmnd->ref_cnt),
|
||||
cmnd->pdu.bhs.sn, cmnd->parent_req, cmnd->pending);
|
||||
#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION)
|
||||
TRACE_CONN_CLOSE_DBG("net_ref_cnt %d, sg %p",
|
||||
@@ -286,7 +284,6 @@ static void trace_conn_close(struct iscsi_conn *conn)
|
||||
|
||||
sBUG_ON(cmnd->parent_req != NULL);
|
||||
|
||||
spin_lock_bh(&cmnd->rsp_cmd_lock);
|
||||
list_for_each_entry(rsp, &cmnd->rsp_cmd_list,
|
||||
rsp_cmd_list_entry) {
|
||||
TRACE_CONN_CLOSE_DBG(" rsp %p, "
|
||||
@@ -305,7 +302,6 @@ static void trace_conn_close(struct iscsi_conn *conn)
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&cmnd->rsp_cmd_lock);
|
||||
#endif /* CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION */
|
||||
}
|
||||
spin_unlock_bh(&conn->cmd_list_lock);
|
||||
@@ -433,21 +429,27 @@ static void close_conn(struct iscsi_conn *conn)
|
||||
struct iscsi_cmnd *cmnd = conn->read_cmnd;
|
||||
|
||||
if (cmnd->scst_state == ISCSI_CMD_STATE_RX_CMD) {
|
||||
TRACE_DBG("Going to wait for cmnd %p to change state "
|
||||
"from RX_CMD", cmnd);
|
||||
TRACE_CONN_CLOSE_DBG("Going to wait for cmnd %p to "
|
||||
"change state from RX_CMD", cmnd);
|
||||
}
|
||||
wait_event(conn->read_state_waitQ,
|
||||
cmnd->scst_state != ISCSI_CMD_STATE_RX_CMD);
|
||||
|
||||
TRACE_CONN_CLOSE_DBG("Releasing conn->read_cmnd %p (conn %p)",
|
||||
conn->read_cmnd, conn);
|
||||
|
||||
conn->read_cmnd = NULL;
|
||||
conn->read_state = RX_INIT_BHS;
|
||||
req_cmnd_release_force(cmnd, 0);
|
||||
req_cmnd_release_force(cmnd);
|
||||
}
|
||||
|
||||
conn_abort(conn);
|
||||
|
||||
/* ToDo: not the best way to wait */
|
||||
while (atomic_read(&conn->conn_ref_cnt) != 0) {
|
||||
if (conn->conn_tm_active)
|
||||
iscsi_check_tm_data_wait_timeouts(conn, true);
|
||||
|
||||
mutex_lock(&target->target_mutex);
|
||||
spin_lock(&session->sn_lock);
|
||||
if (session->tm_rsp && session->tm_rsp->conn == conn) {
|
||||
@@ -637,6 +639,24 @@ static struct iscsi_cmnd *iscsi_get_send_cmnd(struct iscsi_conn *conn)
|
||||
}
|
||||
spin_unlock_bh(&conn->write_list_lock);
|
||||
|
||||
if (unlikely(test_bit(ISCSI_CMD_ABORTED,
|
||||
&cmnd->parent_req->prelim_compl_flags))) {
|
||||
TRACE_MGMT_DBG("Going to send acmd %p (scst cmd %p, "
|
||||
"state %d, parent_req %p)", cmnd, cmnd->scst_cmd,
|
||||
cmnd->scst_state, cmnd->parent_req);
|
||||
}
|
||||
|
||||
if (unlikely(cmnd_opcode(cmnd) == ISCSI_OP_SCSI_TASK_MGT_RSP)) {
|
||||
struct iscsi_task_mgt_hdr *req_hdr =
|
||||
(struct iscsi_task_mgt_hdr *)&cmnd->parent_req->pdu.bhs;
|
||||
struct iscsi_task_rsp_hdr *rsp_hdr =
|
||||
(struct iscsi_task_rsp_hdr *)&cmnd->pdu.bhs;
|
||||
TRACE_MGMT_DBG("Going to send TM response %p (status %d, "
|
||||
"fn %d, parent_req %p)", cmnd, rsp_hdr->response,
|
||||
req_hdr->function & ISCSI_FUNCTION_MASK,
|
||||
cmnd->parent_req);
|
||||
}
|
||||
|
||||
return cmnd;
|
||||
}
|
||||
|
||||
@@ -702,8 +722,10 @@ restart:
|
||||
TRACE_DBG("ERESTARTSYS received for conn %p", conn);
|
||||
goto restart;
|
||||
default:
|
||||
PRINT_ERROR("sock_recvmsg() failed: %d", res);
|
||||
mark_conn_closed(conn);
|
||||
if (!conn->closing) {
|
||||
PRINT_ERROR("sock_recvmsg() failed: %d", res);
|
||||
mark_conn_closed(conn);
|
||||
}
|
||||
if (res == 0)
|
||||
res = -EIO;
|
||||
break;
|
||||
@@ -970,9 +992,15 @@ static void scst_do_job_rd(void)
|
||||
|
||||
spin_lock_bh(&iscsi_rd_lock);
|
||||
|
||||
if (closed)
|
||||
if (unlikely(closed))
|
||||
continue;
|
||||
|
||||
if (unlikely(conn->conn_tm_active)) {
|
||||
spin_unlock_bh(&iscsi_rd_lock);
|
||||
iscsi_check_tm_data_wait_timeouts(conn, false);
|
||||
spin_lock_bh(&iscsi_rd_lock);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SCST_EXTRACHECKS
|
||||
conn->rd_task = NULL;
|
||||
#endif
|
||||
@@ -1107,7 +1135,76 @@ static inline void __iscsi_get_page_callback(struct iscsi_cmnd *cmd) {}
|
||||
static inline void __iscsi_put_page_callback(struct iscsi_cmnd *cmd) {}
|
||||
#endif
|
||||
|
||||
/* This is partially taken from the Ardis code. */
|
||||
void req_add_to_write_timeout_list(struct iscsi_cmnd *req)
|
||||
{
|
||||
struct iscsi_conn *conn;
|
||||
unsigned long timeout_time;
|
||||
bool set_conn_tm_active = false;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (req->on_write_timeout_list)
|
||||
goto out;
|
||||
|
||||
conn = req->conn;
|
||||
|
||||
TRACE_DBG("Adding req %p to conn %p write_timeout_list",
|
||||
req, conn);
|
||||
|
||||
spin_lock_bh(&conn->write_list_lock);
|
||||
|
||||
req->on_write_timeout_list = 1;
|
||||
req->write_start = jiffies;
|
||||
list_add_tail(&req->write_timeout_list_entry,
|
||||
&conn->write_timeout_list);
|
||||
|
||||
if (!timer_pending(&conn->rsp_timer)) {
|
||||
if (unlikely(conn->conn_tm_active ||
|
||||
test_bit(ISCSI_CMD_ABORTED,
|
||||
&req->prelim_compl_flags))) {
|
||||
set_conn_tm_active = true;
|
||||
timeout_time = req->write_start +
|
||||
ISCSI_TM_DATA_WAIT_SCHED_TIMEOUT;
|
||||
} else
|
||||
timeout_time = req->write_start +
|
||||
ISCSI_RSP_SCHED_TIMEOUT;
|
||||
|
||||
TRACE_DBG("Starting timer on %ld (con %p, write_start %ld)",
|
||||
timeout_time, conn, req->write_start);
|
||||
|
||||
conn->rsp_timer.expires = timeout_time;
|
||||
add_timer(&conn->rsp_timer);
|
||||
} else if (unlikely(test_bit(ISCSI_CMD_ABORTED,
|
||||
&req->prelim_compl_flags))) {
|
||||
unsigned long timeout_time = jiffies +
|
||||
ISCSI_TM_DATA_WAIT_SCHED_TIMEOUT;
|
||||
set_conn_tm_active = true;
|
||||
if (time_after(conn->rsp_timer.expires, timeout_time)) {
|
||||
TRACE_MGMT_DBG("Mod timer on %ld (conn %p)",
|
||||
timeout_time, conn);
|
||||
mod_timer(&conn->rsp_timer, timeout_time);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_bh(&conn->write_list_lock);
|
||||
|
||||
/*
|
||||
* conn_tm_active can be already cleared by
|
||||
* iscsi_check_tm_data_wait_timeouts(). write_list_lock is an inner
|
||||
* lock for iscsi_rd_lock.
|
||||
*/
|
||||
if (unlikely(set_conn_tm_active)) {
|
||||
spin_lock_bh(&iscsi_rd_lock);
|
||||
TRACE_MGMT_DBG("Setting conn_tm_active for conn %p", conn);
|
||||
conn->conn_tm_active = 1;
|
||||
spin_unlock_bh(&iscsi_rd_lock);
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
static int write_data(struct iscsi_conn *conn)
|
||||
{
|
||||
mm_segment_t oldfs;
|
||||
@@ -1130,7 +1227,7 @@ static int write_data(struct iscsi_conn *conn)
|
||||
|
||||
iscsi_extracheck_is_wr_thread(conn);
|
||||
|
||||
if (write_cmnd->own_sg == 0) {
|
||||
if (!write_cmnd->own_sg) {
|
||||
ref_cmd = write_cmnd->parent_req;
|
||||
ref_cmd_to_parent = true;
|
||||
} else {
|
||||
@@ -1138,28 +1235,7 @@ static int write_data(struct iscsi_conn *conn)
|
||||
ref_cmd_to_parent = false;
|
||||
}
|
||||
|
||||
if (!ref_cmd->on_written_list) {
|
||||
TRACE_DBG("Adding cmd %p to conn %p written_list", ref_cmd,
|
||||
conn);
|
||||
spin_lock_bh(&conn->write_list_lock);
|
||||
ref_cmd->on_written_list = 1;
|
||||
ref_cmd->write_timeout = jiffies + ISCSI_RSP_TIMEOUT;
|
||||
list_add_tail(&ref_cmd->written_list_entry,
|
||||
&conn->written_list);
|
||||
spin_unlock_bh(&conn->write_list_lock);
|
||||
}
|
||||
|
||||
if (!timer_pending(&conn->rsp_timer)) {
|
||||
sBUG_ON(!ref_cmd->on_written_list);
|
||||
spin_lock_bh(&conn->write_list_lock);
|
||||
if (likely(!timer_pending(&conn->rsp_timer))) {
|
||||
TRACE_DBG("Starting timer on %ld (conn %p)",
|
||||
ref_cmd->write_timeout, conn);
|
||||
conn->rsp_timer.expires = ref_cmd->write_timeout;
|
||||
add_timer(&conn->rsp_timer);
|
||||
}
|
||||
spin_unlock_bh(&conn->write_list_lock);
|
||||
}
|
||||
req_add_to_write_timeout_list(write_cmnd->parent_req);
|
||||
|
||||
file = conn->file;
|
||||
size = conn->write_size;
|
||||
|
||||
@@ -91,9 +91,9 @@ static int iscsi_session_alloc(struct iscsi_target *target,
|
||||
|
||||
spin_lock_init(&session->sn_lock);
|
||||
|
||||
spin_lock_init(&session->cmnd_hash_lock);
|
||||
for (i = 0; i < ARRAY_SIZE(session->cmnd_hash); i++)
|
||||
INIT_LIST_HEAD(&session->cmnd_hash[i]);
|
||||
spin_lock_init(&session->cmnd_data_wait_hash_lock);
|
||||
for (i = 0; i < ARRAY_SIZE(session->cmnd_data_wait_hash); i++)
|
||||
INIT_LIST_HEAD(&session->cmnd_data_wait_hash[i]);
|
||||
|
||||
session->next_ttt = 1;
|
||||
|
||||
@@ -274,8 +274,8 @@ int session_free(struct iscsi_session *session, bool del)
|
||||
sBUG();
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(session->cmnd_hash); i++)
|
||||
sBUG_ON(!list_empty(&session->cmnd_hash[i]));
|
||||
for (i = 0; i < ARRAY_SIZE(session->cmnd_data_wait_hash); i++)
|
||||
sBUG_ON(!list_empty(&session->cmnd_data_wait_hash[i]));
|
||||
|
||||
if (session->sess_reinst_successor != NULL)
|
||||
sess_reinst_finished(session->sess_reinst_successor);
|
||||
|
||||
@@ -302,9 +302,9 @@ struct iscsi_key session_keys[] = {
|
||||
{"MaxXmitDataSegmentLength", 8192, -1, 512, -1, &minimum_ops},
|
||||
{"MaxBurstLength", 262144, -1, 512, -1, &minimum_ops},
|
||||
{"FirstBurstLength", 65536, -1, 512, -1, &minimum_ops},
|
||||
{"DefaultTime2Wait", 2, 2, 0, 3600, &maximum_ops},
|
||||
{"DefaultTime2Retain", 20, 20, 0, 3600, &minimum_ops},
|
||||
{"MaxOutstandingR2T", 1, 20, 1, 65535, &minimum_ops},
|
||||
{"DefaultTime2Wait", 2, 0, 0, 3600, &maximum_ops},
|
||||
{"DefaultTime2Retain", 20, 0, 0, 3600, &minimum_ops},
|
||||
{"MaxOutstandingR2T", 1, 32, 1, 65535, &minimum_ops},
|
||||
{"DataPDUInOrder", 1, 0, 0, 1, &or_ops},
|
||||
{"DataSequenceInOrder", 1, 0, 0, 1, &or_ops},
|
||||
{"ErrorRecoveryLevel", 0, 0, 0, 0, &minimum_ops},
|
||||
|
||||
@@ -115,41 +115,44 @@ static inline int list_is_last(const struct list_head *list,
|
||||
/* Allocation of the cmd's data buffer */
|
||||
#define SCST_CMD_STATE_PREPARE_SPACE 2
|
||||
|
||||
/* Calling preprocessing_done() */
|
||||
#define SCST_CMD_STATE_PREPROCESSING_DONE 3
|
||||
|
||||
/* Target driver's rdy_to_xfer() is going to be called */
|
||||
#define SCST_CMD_STATE_RDY_TO_XFER 3
|
||||
#define SCST_CMD_STATE_RDY_TO_XFER 4
|
||||
|
||||
/* Target driver's pre_exec() is going to be called */
|
||||
#define SCST_CMD_STATE_TGT_PRE_EXEC 4
|
||||
#define SCST_CMD_STATE_TGT_PRE_EXEC 5
|
||||
|
||||
/* Cmd is going to be sent for execution */
|
||||
#define SCST_CMD_STATE_SEND_FOR_EXEC 5
|
||||
#define SCST_CMD_STATE_SEND_FOR_EXEC 6
|
||||
|
||||
/* Cmd is being checked if it should be executed locally */
|
||||
#define SCST_CMD_STATE_LOCAL_EXEC 6
|
||||
#define SCST_CMD_STATE_LOCAL_EXEC 7
|
||||
|
||||
/* Cmd is ready for execution */
|
||||
#define SCST_CMD_STATE_REAL_EXEC 7
|
||||
#define SCST_CMD_STATE_REAL_EXEC 8
|
||||
|
||||
/* Internal post-exec checks */
|
||||
#define SCST_CMD_STATE_PRE_DEV_DONE 8
|
||||
#define SCST_CMD_STATE_PRE_DEV_DONE 9
|
||||
|
||||
/* Internal MODE SELECT pages related checks */
|
||||
#define SCST_CMD_STATE_MODE_SELECT_CHECKS 9
|
||||
#define SCST_CMD_STATE_MODE_SELECT_CHECKS 10
|
||||
|
||||
/* Dev handler's dev_done() is going to be called */
|
||||
#define SCST_CMD_STATE_DEV_DONE 10
|
||||
#define SCST_CMD_STATE_DEV_DONE 11
|
||||
|
||||
/* Target driver's xmit_response() is going to be called */
|
||||
#define SCST_CMD_STATE_PRE_XMIT_RESP 11
|
||||
#define SCST_CMD_STATE_PRE_XMIT_RESP 12
|
||||
|
||||
/* Target driver's xmit_response() is going to be called */
|
||||
#define SCST_CMD_STATE_XMIT_RESP 12
|
||||
#define SCST_CMD_STATE_XMIT_RESP 13
|
||||
|
||||
/* Cmd finished */
|
||||
#define SCST_CMD_STATE_FINISHED 13
|
||||
#define SCST_CMD_STATE_FINISHED 14
|
||||
|
||||
/* Internal cmd finished */
|
||||
#define SCST_CMD_STATE_FINISHED_INTERNAL 14
|
||||
#define SCST_CMD_STATE_FINISHED_INTERNAL 15
|
||||
|
||||
#define SCST_CMD_STATE_LAST_ACTIVE (SCST_CMD_STATE_FINISHED_INTERNAL+100)
|
||||
|
||||
@@ -159,8 +162,8 @@ static inline int list_is_last(const struct list_head *list,
|
||||
/* LUN translation (cmd->tgt_dev assignment) */
|
||||
#define SCST_CMD_STATE_INIT (SCST_CMD_STATE_LAST_ACTIVE+2)
|
||||
|
||||
/* Allocation of the cmd's data buffer */
|
||||
#define SCST_CMD_STATE_PREPROCESS_DONE (SCST_CMD_STATE_LAST_ACTIVE+3)
|
||||
/* Waiting for scst_restart_cmd() */
|
||||
#define SCST_CMD_STATE_PREPROCESSING_DONE_CALLED (SCST_CMD_STATE_LAST_ACTIVE+3)
|
||||
|
||||
/* Waiting for data from the initiator (until scst_rx_data() called) */
|
||||
#define SCST_CMD_STATE_DATA_WAIT (SCST_CMD_STATE_LAST_ACTIVE+4)
|
||||
@@ -704,6 +707,8 @@ struct scst_tgt_template {
|
||||
* A target driver could need to do some actions at this stage.
|
||||
* After the target driver done the needed actions, it shall call
|
||||
* scst_restart_cmd() in order to continue processing this command.
|
||||
* In case of preliminary the command completion, this function will
|
||||
* also be called before xmit_response().
|
||||
*
|
||||
* Called only if the cmd is queued using scst_cmd_init_stage1_done()
|
||||
* instead of scst_cmd_init_done().
|
||||
@@ -1212,19 +1217,15 @@ struct scst_session {
|
||||
struct list_head sess_tgt_dev_list_hash[TGT_DEV_HASH_SIZE];
|
||||
|
||||
/*
|
||||
* List of cmds in this session. Used to find a cmd in the
|
||||
* session. Protected by sess_list_lock.
|
||||
* List of cmds in this session. Protected by sess_list_lock.
|
||||
*
|
||||
* We must always keep commands in the sess list from the
|
||||
* very beginning, because otherwise they can be missed during
|
||||
* TM processing.
|
||||
*/
|
||||
struct list_head search_cmd_list;
|
||||
struct list_head sess_cmd_list;
|
||||
|
||||
spinlock_t sess_list_lock; /* protects search_cmd_list, etc */
|
||||
|
||||
/*
|
||||
* List of cmds in this in the state after PRE_XMIT_RESP. All the cmds
|
||||
* moved here from search_cmd_list. Needed for hw_pending_work.
|
||||
* Protected by sess_list_lock.
|
||||
*/
|
||||
struct list_head after_pre_xmit_cmd_list;
|
||||
spinlock_t sess_list_lock; /* protects sess_cmd_list, etc */
|
||||
|
||||
atomic_t refcnt; /* get/put counter */
|
||||
|
||||
@@ -1484,7 +1485,7 @@ struct scst_cmd {
|
||||
/* The corresponding sn_slot in tgt_dev->sn_slots */
|
||||
atomic_t *sn_slot;
|
||||
|
||||
/* List entry for sess's search_cmd_list and after_pre_xmit_cmd_list */
|
||||
/* List entry for sess's sess_cmd_list */
|
||||
struct list_head sess_cmd_list_entry;
|
||||
|
||||
/*
|
||||
@@ -2326,14 +2327,18 @@ static inline int scst_rx_mgmt_fn_lun(struct scst_session *sess, int fn,
|
||||
int scst_get_cdb_info(struct scst_cmd *cmd);
|
||||
|
||||
/*
|
||||
* Set error SCSI status in the command and prepares it for returning it
|
||||
* Set error SCSI status in the command and prepares it for returning it.
|
||||
*
|
||||
* Returns 0 on success, error code otherwise.
|
||||
*/
|
||||
void scst_set_cmd_error_status(struct scst_cmd *cmd, int status);
|
||||
int scst_set_cmd_error_status(struct scst_cmd *cmd, int status);
|
||||
|
||||
/*
|
||||
* Set error in the command and fill the sense buffer
|
||||
* Set error in the command and fill the sense buffer.
|
||||
*
|
||||
* Returns 0 on success, error code otherwise.
|
||||
*/
|
||||
void scst_set_cmd_error(struct scst_cmd *cmd, int key, int asc, int ascq);
|
||||
int scst_set_cmd_error(struct scst_cmd *cmd, int key, int asc, int ascq);
|
||||
|
||||
/*
|
||||
* Sets BUSY or TASK QUEUE FULL status
|
||||
@@ -2490,6 +2495,14 @@ static inline int scst_cmd_atomic(struct scst_cmd *cmd)
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns TRUE if cmd has been completed.
|
||||
*/
|
||||
static inline int scst_cmd_completed(struct scst_cmd *cmd)
|
||||
{
|
||||
return cmd->completed;
|
||||
}
|
||||
|
||||
static inline enum scst_exec_context __scst_estimate_context(bool direct)
|
||||
{
|
||||
if (in_irq())
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
#define TRACE_MGMT_DEBUG 0x00001000
|
||||
#define TRACE_SCSI 0x00002000
|
||||
#define TRACE_SPECIAL 0x00004000 /* filtering debug, etc */
|
||||
#define TRACE_FLOW_CONTROL 0x00008000 /* flow control in action */
|
||||
#define TRACE_ALL 0xffffffff
|
||||
/* Flags 0xXXXX0000 are local for users */
|
||||
|
||||
@@ -308,11 +309,13 @@ do { \
|
||||
#define TRACE_MEM(format, args...) do {} while (0)
|
||||
#define TRACE_SG(format, args...) do {} while (0)
|
||||
#define TRACE_DBG(format, args...) do {} while (0)
|
||||
#define TRACE_DBG_FLAG(format, args...) do {} while (0)
|
||||
#define TRACE_DBG_SPECIAL(format, args...) do {} while (0)
|
||||
#define TRACE_MGMT_DBG(format, args...) do {} while (0)
|
||||
#define TRACE_MGMT_DBG_SPECIAL(format, args...) do {} while (0)
|
||||
#define TRACE_BUFFER(message, buff, len) do {} while (0)
|
||||
#define TRACE_BUFF_FLAG(flag, message, buff, len) do {} while (0)
|
||||
|
||||
#ifndef GENERATING_UPSTREAM_PATCH
|
||||
#define TRACE_ENTRY() do {} while (0)
|
||||
#define TRACE_EXIT() do {} while (0)
|
||||
|
||||
@@ -554,7 +554,7 @@ static const struct scst_sdbops scst_scsi_op_table[] = {
|
||||
SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_cdb_len_10}
|
||||
};
|
||||
|
||||
#define SCST_CDB_TBL_SIZE ARRAY_SIZE(scst_scsi_op_table)
|
||||
#define SCST_CDB_TBL_SIZE ((int)ARRAY_SIZE(scst_scsi_op_table))
|
||||
|
||||
static void scst_free_tgt_dev(struct scst_tgt_dev *tgt_dev);
|
||||
static void scst_check_internal_sense(struct scst_device *dev, int result,
|
||||
@@ -620,6 +620,11 @@ int scst_alloc_set_sense(struct scst_cmd *cmd, int atomic,
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
/*
|
||||
* We don't check here if the existing sense is valid or not, because
|
||||
* we suppose the caller did it based on cmd->status.
|
||||
*/
|
||||
|
||||
res = scst_alloc_sense(cmd, atomic);
|
||||
if (res != 0) {
|
||||
PRINT_BUFFER("Lost sense", sense, len);
|
||||
@@ -642,10 +647,19 @@ out:
|
||||
}
|
||||
EXPORT_SYMBOL(scst_alloc_set_sense);
|
||||
|
||||
void scst_set_cmd_error_status(struct scst_cmd *cmd, int status)
|
||||
int scst_set_cmd_error_status(struct scst_cmd *cmd, int status)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (cmd->status != 0) {
|
||||
TRACE_MGMT_DBG("cmd %p already has status %x set", cmd,
|
||||
cmd->status);
|
||||
res = -EEXIST;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cmd->status = status;
|
||||
cmd->host_status = DID_OK;
|
||||
|
||||
@@ -658,21 +672,24 @@ void scst_set_cmd_error_status(struct scst_cmd *cmd, int status)
|
||||
|
||||
cmd->completed = 1;
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(scst_set_cmd_error_status);
|
||||
|
||||
void scst_set_cmd_error(struct scst_cmd *cmd, int key, int asc, int ascq)
|
||||
int scst_set_cmd_error(struct scst_cmd *cmd, int key, int asc, int ascq)
|
||||
{
|
||||
int rc;
|
||||
int res;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
scst_set_cmd_error_status(cmd, SAM_STAT_CHECK_CONDITION);
|
||||
res = scst_set_cmd_error_status(cmd, SAM_STAT_CHECK_CONDITION);
|
||||
if (res != 0)
|
||||
goto out;
|
||||
|
||||
rc = scst_alloc_sense(cmd, 1);
|
||||
if (rc != 0) {
|
||||
res = scst_alloc_sense(cmd, 1);
|
||||
if (res != 0) {
|
||||
PRINT_ERROR("Lost sense data (key %x, asc %x, ascq %x)",
|
||||
key, asc, ascq);
|
||||
goto out;
|
||||
@@ -683,8 +700,8 @@ void scst_set_cmd_error(struct scst_cmd *cmd, int key, int asc, int ascq)
|
||||
TRACE_BUFFER("Sense set", cmd->sense, cmd->sense_valid_len);
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(scst_set_cmd_error);
|
||||
|
||||
@@ -871,16 +888,22 @@ out:
|
||||
}
|
||||
EXPORT_SYMBOL(scst_check_convert_sense);
|
||||
|
||||
static void scst_set_cmd_error_sense(struct scst_cmd *cmd, uint8_t *sense,
|
||||
static int scst_set_cmd_error_sense(struct scst_cmd *cmd, uint8_t *sense,
|
||||
unsigned int len)
|
||||
{
|
||||
int res;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
scst_set_cmd_error_status(cmd, SAM_STAT_CHECK_CONDITION);
|
||||
scst_alloc_set_sense(cmd, 1, sense, len);
|
||||
res = scst_set_cmd_error_status(cmd, SAM_STAT_CHECK_CONDITION);
|
||||
if (res != 0)
|
||||
goto out;
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
res = scst_alloc_set_sense(cmd, 1, sense, len);
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
void scst_set_busy(struct scst_cmd *cmd)
|
||||
@@ -891,13 +914,13 @@ void scst_set_busy(struct scst_cmd *cmd)
|
||||
|
||||
if ((c <= 1) || (cmd->sess->init_phase != SCST_SESS_IPH_READY)) {
|
||||
scst_set_cmd_error_status(cmd, SAM_STAT_BUSY);
|
||||
TRACE(TRACE_MGMT_MINOR, "Sending BUSY status to initiator %s "
|
||||
TRACE(TRACE_FLOW_CONTROL, "Sending BUSY status to initiator %s "
|
||||
"(cmds count %d, queue_type %x, sess->init_phase %d)",
|
||||
cmd->sess->initiator_name, c,
|
||||
cmd->queue_type, cmd->sess->init_phase);
|
||||
} else {
|
||||
scst_set_cmd_error_status(cmd, SAM_STAT_TASK_SET_FULL);
|
||||
TRACE(TRACE_MGMT_MINOR, "Sending QUEUE_FULL status to "
|
||||
TRACE(TRACE_FLOW_CONTROL, "Sending QUEUE_FULL status to "
|
||||
"initiator %s (cmds count %d, queue_type %x, "
|
||||
"sess->init_phase %d)", cmd->sess->initiator_name, c,
|
||||
cmd->queue_type, cmd->sess->init_phase);
|
||||
@@ -996,7 +1019,7 @@ static void scst_free_aen(struct scst_aen *aen)
|
||||
return;
|
||||
};
|
||||
|
||||
/* Must be called unded scst_mutex */
|
||||
/* Must be called under scst_mutex */
|
||||
void scst_gen_aen_or_ua(struct scst_tgt_dev *tgt_dev,
|
||||
int key, int asc, int ascq)
|
||||
{
|
||||
@@ -1473,6 +1496,10 @@ int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd)
|
||||
case SCST_CMD_STATE_INIT:
|
||||
case SCST_CMD_STATE_PRE_PARSE:
|
||||
case SCST_CMD_STATE_DEV_PARSE:
|
||||
if (cmd->preprocessing_only) {
|
||||
res = SCST_CMD_STATE_PREPROCESSING_DONE;
|
||||
break;
|
||||
} /* else go through */
|
||||
case SCST_CMD_STATE_DEV_DONE:
|
||||
if (cmd->internal)
|
||||
res = SCST_CMD_STATE_FINISHED_INTERNAL;
|
||||
@@ -1489,8 +1516,19 @@ int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd)
|
||||
res = SCST_CMD_STATE_XMIT_RESP;
|
||||
break;
|
||||
|
||||
case SCST_CMD_STATE_PREPROCESS_DONE:
|
||||
case SCST_CMD_STATE_PREPROCESSING_DONE:
|
||||
case SCST_CMD_STATE_PREPROCESSING_DONE_CALLED:
|
||||
if (cmd->tgt_dev == NULL)
|
||||
res = SCST_CMD_STATE_PRE_XMIT_RESP;
|
||||
else
|
||||
res = SCST_CMD_STATE_PRE_DEV_DONE;
|
||||
break;
|
||||
|
||||
case SCST_CMD_STATE_PREPARE_SPACE:
|
||||
if (cmd->preprocessing_only) {
|
||||
res = SCST_CMD_STATE_PREPROCESSING_DONE;
|
||||
break;
|
||||
} /* else go through */
|
||||
case SCST_CMD_STATE_RDY_TO_XFER:
|
||||
case SCST_CMD_STATE_DATA_WAIT:
|
||||
case SCST_CMD_STATE_TGT_PRE_EXEC:
|
||||
@@ -1533,7 +1571,8 @@ void scst_set_cmd_abnormal_done_state(struct scst_cmd *cmd)
|
||||
cmd->state = scst_get_cmd_abnormal_done_state(cmd);
|
||||
|
||||
#ifdef CONFIG_SCST_EXTRACHECKS
|
||||
if ((cmd->state != SCST_CMD_STATE_PRE_XMIT_RESP) &&
|
||||
if (((cmd->state != SCST_CMD_STATE_PRE_XMIT_RESP) &&
|
||||
(cmd->state != SCST_CMD_STATE_PREPROCESSING_DONE)) &&
|
||||
(cmd->tgt_dev == NULL) && !cmd->internal) {
|
||||
PRINT_CRIT_ERROR("Wrong not inited cmd state %d (cmd %p, "
|
||||
"op %x)", cmd->state, cmd, cmd->cdb[0]);
|
||||
@@ -1706,8 +1745,7 @@ static void scst_hw_pending_work_fn(struct delayed_work *work)
|
||||
spin_lock_irqsave(&sess->sess_list_lock, flags);
|
||||
|
||||
restart:
|
||||
list_for_each_entry(cmd, &sess->search_cmd_list,
|
||||
sess_cmd_list_entry) {
|
||||
list_for_each_entry(cmd, &sess->sess_cmd_list, sess_cmd_list_entry) {
|
||||
int rc;
|
||||
|
||||
rc = scst_check_hw_pending_cmd(cmd, cur_time, max_time, sess,
|
||||
@@ -1720,23 +1758,7 @@ restart:
|
||||
goto restart;
|
||||
}
|
||||
|
||||
restart1:
|
||||
list_for_each_entry(cmd, &sess->after_pre_xmit_cmd_list,
|
||||
sess_cmd_list_entry) {
|
||||
int rc;
|
||||
|
||||
rc = scst_check_hw_pending_cmd(cmd, cur_time, max_time, sess,
|
||||
&flags, tgtt);
|
||||
if (rc < 0)
|
||||
break;
|
||||
else if (rc == 0)
|
||||
continue;
|
||||
else
|
||||
goto restart1;
|
||||
}
|
||||
|
||||
if (!list_empty(&sess->search_cmd_list) ||
|
||||
!list_empty(&sess->after_pre_xmit_cmd_list)) {
|
||||
if (list_empty(&sess->sess_cmd_list)) {
|
||||
/*
|
||||
* For stuck cmds if there is no activity we might need to have
|
||||
* one more run to release them, so reschedule once again.
|
||||
@@ -2438,7 +2460,7 @@ int scst_acg_add_name(struct scst_acg *acg, const char *name)
|
||||
if (strcmp(n->name, name) == 0) {
|
||||
PRINT_ERROR("Name %s already exists in group %s",
|
||||
name, acg->acg_name);
|
||||
res = -EINVAL;
|
||||
res = -EEXIST;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -2834,8 +2856,7 @@ struct scst_session *scst_alloc_session(struct scst_tgt *tgt, gfp_t gfp_mask,
|
||||
INIT_LIST_HEAD(sess_tgt_dev_list_head);
|
||||
}
|
||||
spin_lock_init(&sess->sess_list_lock);
|
||||
INIT_LIST_HEAD(&sess->search_cmd_list);
|
||||
INIT_LIST_HEAD(&sess->after_pre_xmit_cmd_list);
|
||||
INIT_LIST_HEAD(&sess->sess_cmd_list);
|
||||
sess->tgt = tgt;
|
||||
INIT_LIST_HEAD(&sess->init_deferred_cmd_list);
|
||||
INIT_LIST_HEAD(&sess->init_deferred_mcmd_list);
|
||||
@@ -3867,7 +3888,7 @@ int scst_get_cdb_info(struct scst_cmd *cmd)
|
||||
|
||||
op = cmd->cdb[0]; /* get clear opcode */
|
||||
|
||||
TRACE_DBG("opcode=%02x, cdblen=%d bytes, tblsize=%zd, "
|
||||
TRACE_DBG("opcode=%02x, cdblen=%d bytes, tblsize=%d, "
|
||||
"dev_type=%d", op, SCST_GET_CDB_LEN(op), SCST_CDB_TBL_SIZE,
|
||||
dev_type);
|
||||
|
||||
@@ -4676,9 +4697,9 @@ void scst_process_reset(struct scst_device *dev,
|
||||
|
||||
spin_lock_irq(&sess->sess_list_lock);
|
||||
|
||||
TRACE_DBG("Searching in search cmd list (sess=%p)", sess);
|
||||
list_for_each_entry(cmd, &sess->search_cmd_list,
|
||||
sess_cmd_list_entry) {
|
||||
TRACE_DBG("Searching in sess cmd list (sess=%p)", sess);
|
||||
list_for_each_entry(cmd, &sess->sess_cmd_list,
|
||||
sess_cmd_list_entry) {
|
||||
if (cmd == exclude_cmd)
|
||||
continue;
|
||||
if ((cmd->tgt_dev == tgt_dev) ||
|
||||
@@ -4736,7 +4757,7 @@ again:
|
||||
TRACE_DBG("%s",
|
||||
"SCST_TGT_DEV_UA_PENDING set, but UA_list empty");
|
||||
res = -1;
|
||||
goto out_unlock;
|
||||
goto out_unlock_tgt_dev_lock;
|
||||
}
|
||||
|
||||
UA_entry = list_entry(cmd->tgt_dev->UA_list.next, typeof(*UA_entry),
|
||||
@@ -4774,8 +4795,9 @@ again:
|
||||
goto again;
|
||||
}
|
||||
|
||||
scst_set_cmd_error_sense(cmd, UA_entry->UA_sense_buffer,
|
||||
UA_entry->UA_valid_sense_len);
|
||||
if (scst_set_cmd_error_sense(cmd, UA_entry->UA_sense_buffer,
|
||||
UA_entry->UA_valid_sense_len) != 0)
|
||||
goto out_unlock;
|
||||
|
||||
cmd->ua_ignore = 1;
|
||||
|
||||
@@ -4831,6 +4853,7 @@ out_unlock:
|
||||
spin_lock_bh(&cmd->tgt_dev->tgt_dev_lock);
|
||||
}
|
||||
|
||||
out_unlock_tgt_dev_lock:
|
||||
spin_unlock_bh(&cmd->tgt_dev->tgt_dev_lock);
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
@@ -5600,7 +5623,6 @@ static void tm_dbg_init_tgt_dev(struct scst_tgt_dev *tgt_dev,
|
||||
if (tm_dbg_tgt_dev != NULL)
|
||||
tm_dbg_deinit_tgt_dev(tm_dbg_tgt_dev);
|
||||
|
||||
/* Do TM debugging only for LUN 0 */
|
||||
spin_lock_irqsave(&scst_tm_dbg_lock, flags);
|
||||
tm_dbg_state = INIT_TM_DBG_STATE;
|
||||
tm_dbg_on_state_passes =
|
||||
|
||||
@@ -110,6 +110,7 @@ static struct scst_trace_log scst_proc_trace_tbl[] = {
|
||||
{ TRACE_MGMT, "mgmt" },
|
||||
{ TRACE_MGMT_MINOR, "mgmt_minor" },
|
||||
{ TRACE_MGMT_DEBUG, "mgmt_dbg" },
|
||||
{ TRACE_FLOW_CONTROL, "flow_control" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
@@ -1975,7 +1976,7 @@ static ssize_t scst_proc_groups_devices_write(struct file *file,
|
||||
if (action == SCST_PROC_ACTION_ADD) {
|
||||
PRINT_ERROR("virt lun %d already exists in "
|
||||
"group %s", virt_lun, acg->acg_name);
|
||||
res = -EINVAL;
|
||||
res = -EEXIST;
|
||||
goto out_free_up;
|
||||
} else {
|
||||
/* Replace */
|
||||
@@ -2324,15 +2325,26 @@ static int scst_sessions_info_show(struct seq_file *seq, void *v)
|
||||
|
||||
seq_printf(seq, "%-20s %-45s %-35s %-15s\n",
|
||||
"Target name", "Initiator name",
|
||||
"Group name", "Command Count");
|
||||
"Group name", "Active/All Commands Count");
|
||||
|
||||
list_for_each_entry(acg, &scst_acg_list, scst_acg_list_entry) {
|
||||
list_for_each_entry(sess, &acg->acg_sess_list,
|
||||
acg_sess_list_entry) {
|
||||
seq_printf(seq, "%-20s %-45s %-35s %-15d\n",
|
||||
acg_sess_list_entry) {
|
||||
int active_cmds = 0, t;
|
||||
for (t = TGT_DEV_HASH_SIZE-1; t >= 0; t--) {
|
||||
struct list_head *sess_tgt_dev_list_head =
|
||||
&sess->sess_tgt_dev_list_hash[t];
|
||||
struct scst_tgt_dev *tgt_dev;
|
||||
list_for_each_entry(tgt_dev,
|
||||
sess_tgt_dev_list_head,
|
||||
sess_tgt_dev_list_entry) {
|
||||
active_cmds += atomic_read(&tgt_dev->tgt_dev_cmd_count);
|
||||
}
|
||||
}
|
||||
seq_printf(seq, "%-20s %-45s %-35s %d/%d\n",
|
||||
sess->tgt->tgtt->name,
|
||||
sess->initiator_name,
|
||||
acg->acg_name,
|
||||
acg->acg_name, active_cmds,
|
||||
atomic_read(&sess->sess_cmd_count));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ static struct scst_trace_log scst_trace_tbl[] = {
|
||||
{ TRACE_MGMT, "mgmt" },
|
||||
{ TRACE_MGMT_MINOR, "mgmt_minor" },
|
||||
{ TRACE_MGMT_DEBUG, "mgmt_dbg" },
|
||||
{ TRACE_FLOW_CONTROL, "flow_control" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
@@ -770,7 +771,7 @@ void scst_device_sysfs_put(struct scst_device *dev)
|
||||
* Target sessions directory implementation
|
||||
*/
|
||||
|
||||
ssize_t scst_sess_sysfs_commands_show(struct kobject *kobj,
|
||||
static ssize_t scst_sess_sysfs_commands_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
struct scst_session *sess;
|
||||
@@ -783,7 +784,42 @@ ssize_t scst_sess_sysfs_commands_show(struct kobject *kobj,
|
||||
static struct kobj_attribute session_commands_attr =
|
||||
__ATTR(commands, S_IRUGO, scst_sess_sysfs_commands_show, NULL);
|
||||
|
||||
ssize_t scst_sess_sysfs_initiator_name_show(struct kobject *kobj,
|
||||
static ssize_t scst_sess_sysfs_active_commands_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
int res;
|
||||
struct scst_session *sess;
|
||||
int active_cmds = 0, t;
|
||||
|
||||
if (mutex_lock_interruptible(&scst_mutex) != 0) {
|
||||
res = -EINTR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sess = container_of(kobj, struct scst_session, sess_kobj);
|
||||
|
||||
for (t = TGT_DEV_HASH_SIZE-1; t >= 0; t--) {
|
||||
struct list_head *sess_tgt_dev_list_head =
|
||||
&sess->sess_tgt_dev_list_hash[t];
|
||||
struct scst_tgt_dev *tgt_dev;
|
||||
list_for_each_entry(tgt_dev, sess_tgt_dev_list_head,
|
||||
sess_tgt_dev_list_entry) {
|
||||
active_cmds += atomic_read(&tgt_dev->tgt_dev_cmd_count);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&scst_mutex);
|
||||
|
||||
res = sprintf(buf, "%i\n", active_cmds);
|
||||
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
|
||||
static struct kobj_attribute session_active_commands_attr =
|
||||
__ATTR(commands, S_IRUGO, scst_sess_sysfs_active_commands_show, NULL);
|
||||
|
||||
static ssize_t scst_sess_sysfs_initiator_name_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
struct scst_session *sess;
|
||||
@@ -799,6 +835,7 @@ static struct kobj_attribute session_initiator_name_attr =
|
||||
|
||||
static struct attribute *scst_session_attrs[] = {
|
||||
&session_commands_attr.attr,
|
||||
&session_active_commands_attr.attr,
|
||||
&session_initiator_name_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
@@ -1241,7 +1278,7 @@ static ssize_t scst_luns_mgmt_store(struct kobject *kobj,
|
||||
if (action == SCST_LUN_ACTION_ADD) {
|
||||
PRINT_ERROR("virt lun %d already exists in "
|
||||
"group %s", virt_lun, acg->acg_name);
|
||||
res = -EINVAL;
|
||||
res = -EEXIST;
|
||||
goto out_free_up;
|
||||
} else {
|
||||
/* Replace */
|
||||
|
||||
@@ -164,6 +164,8 @@ out_redirect:
|
||||
/*
|
||||
* Poor man solution for single threaded targets, where
|
||||
* blocking receiver at least sometimes means blocking all.
|
||||
* For instance, iSCSI target won't be able to receive
|
||||
* Data-Out PDUs.
|
||||
*/
|
||||
sBUG_ON(*context != SCST_CONTEXT_DIRECT);
|
||||
scst_set_busy(cmd);
|
||||
@@ -222,14 +224,14 @@ void scst_cmd_init_done(struct scst_cmd *cmd,
|
||||
|
||||
if (unlikely(sess->init_phase != SCST_SESS_IPH_READY)) {
|
||||
/*
|
||||
* We have to always keep command in the search list from the
|
||||
* very beginning, because otherwise it can be missed during
|
||||
* We must always keep commands in the sess list from the
|
||||
* very beginning, because otherwise they can be missed during
|
||||
* TM processing. This check is needed because there might be
|
||||
* old, i.e. deferred, commands and new, i.e. just coming, ones.
|
||||
*/
|
||||
if (cmd->sess_cmd_list_entry.next == NULL)
|
||||
list_add_tail(&cmd->sess_cmd_list_entry,
|
||||
&sess->search_cmd_list);
|
||||
&sess->sess_cmd_list);
|
||||
switch (sess->init_phase) {
|
||||
case SCST_SESS_IPH_SUCCESS:
|
||||
break;
|
||||
@@ -250,7 +252,7 @@ void scst_cmd_init_done(struct scst_cmd *cmd,
|
||||
}
|
||||
} else
|
||||
list_add_tail(&cmd->sess_cmd_list_entry,
|
||||
&sess->search_cmd_list);
|
||||
&sess->sess_cmd_list);
|
||||
|
||||
spin_unlock_irqrestore(&sess->sess_list_lock, flags);
|
||||
|
||||
@@ -287,12 +289,6 @@ void scst_cmd_init_done(struct scst_cmd *cmd,
|
||||
rc = scst_init_cmd(cmd, &pref_context);
|
||||
if (unlikely(rc < 0))
|
||||
goto out;
|
||||
else if (unlikely(cmd->status == SAM_STAT_CHECK_CONDITION)) {
|
||||
if (rc == 0) {
|
||||
/* Target driver preliminary completed cmd */
|
||||
scst_set_cmd_abnormal_done_state(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
active:
|
||||
/* Here cmd must not be in any cmd list, no locks */
|
||||
@@ -303,12 +299,10 @@ active:
|
||||
|
||||
case SCST_CONTEXT_DIRECT:
|
||||
scst_process_active_cmd(cmd, false);
|
||||
/* For *NEED_THREAD wake_up() is already done */
|
||||
break;
|
||||
|
||||
case SCST_CONTEXT_DIRECT_ATOMIC:
|
||||
scst_process_active_cmd(cmd, true);
|
||||
/* For *NEED_THREAD wake_up() is already done */
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -639,6 +633,7 @@ static int scst_parse_cmd(struct scst_cmd *cmd)
|
||||
}
|
||||
|
||||
set_res:
|
||||
#ifdef CONFIG_SCST_EXTRACHECKS
|
||||
switch (state) {
|
||||
case SCST_CMD_STATE_PREPARE_SPACE:
|
||||
case SCST_CMD_STATE_PRE_PARSE:
|
||||
@@ -654,8 +649,10 @@ set_res:
|
||||
case SCST_CMD_STATE_XMIT_RESP:
|
||||
case SCST_CMD_STATE_FINISHED:
|
||||
case SCST_CMD_STATE_FINISHED_INTERNAL:
|
||||
#endif
|
||||
cmd->state = state;
|
||||
res = SCST_CMD_STATE_RES_CONT_SAME;
|
||||
#ifdef CONFIG_SCST_EXTRACHECKS
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -670,6 +667,7 @@ set_res:
|
||||
}
|
||||
goto out_error;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (cmd->resp_data_len == -1) {
|
||||
if (cmd->data_direction & SCST_DATA_READ)
|
||||
@@ -701,7 +699,7 @@ static int scst_prepare_space(struct scst_cmd *cmd)
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (cmd->data_direction == SCST_DATA_NONE)
|
||||
goto prep_done;
|
||||
goto done;
|
||||
|
||||
if (cmd->tgt_need_alloc_data_buf) {
|
||||
int orig_bufflen = cmd->bufflen;
|
||||
@@ -767,30 +765,10 @@ check:
|
||||
goto out_no_space;
|
||||
}
|
||||
|
||||
prep_done:
|
||||
if (cmd->preprocessing_only) {
|
||||
cmd->preprocessing_only = 0;
|
||||
|
||||
if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))) {
|
||||
TRACE_MGMT_DBG("ABORTED set, returning ABORTED for "
|
||||
"cmd %p", cmd);
|
||||
scst_set_cmd_abnormal_done_state(cmd);
|
||||
res = SCST_CMD_STATE_RES_CONT_SAME;
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = SCST_CMD_STATE_RES_CONT_NEXT;
|
||||
cmd->state = SCST_CMD_STATE_PREPROCESS_DONE;
|
||||
|
||||
TRACE_DBG("Calling preprocessing_done(cmd %p)", cmd);
|
||||
scst_set_cur_start(cmd);
|
||||
cmd->tgtt->preprocessing_done(cmd);
|
||||
TRACE_DBG("%s", "preprocessing_done() returned");
|
||||
goto out;
|
||||
|
||||
}
|
||||
|
||||
if (cmd->data_direction & SCST_DATA_WRITE)
|
||||
done:
|
||||
if (cmd->preprocessing_only)
|
||||
cmd->state = SCST_CMD_STATE_PREPROCESSING_DONE;
|
||||
else if (cmd->data_direction & SCST_DATA_WRITE)
|
||||
cmd->state = SCST_CMD_STATE_RDY_TO_XFER;
|
||||
else
|
||||
cmd->state = SCST_CMD_STATE_TGT_PRE_EXEC;
|
||||
@@ -814,6 +792,28 @@ out_error:
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int scst_preprocessing_done(struct scst_cmd *cmd)
|
||||
{
|
||||
int res;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
EXTRACHECKS_BUG_ON(!cmd->preprocessing_only);
|
||||
|
||||
cmd->preprocessing_only = 0;
|
||||
|
||||
res = SCST_CMD_STATE_RES_CONT_NEXT;
|
||||
cmd->state = SCST_CMD_STATE_PREPROCESSING_DONE_CALLED;
|
||||
|
||||
TRACE_DBG("Calling preprocessing_done(cmd %p)", cmd);
|
||||
scst_set_cur_start(cmd);
|
||||
cmd->tgtt->preprocessing_done(cmd);
|
||||
TRACE_DBG("%s", "preprocessing_done() returned");
|
||||
|
||||
TRACE_EXIT_HRES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
void scst_restart_cmd(struct scst_cmd *cmd, int status,
|
||||
enum scst_exec_context pref_context)
|
||||
{
|
||||
@@ -2777,6 +2777,7 @@ static int scst_dev_done(struct scst_cmd *cmd)
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
#ifdef CONFIG_SCST_EXTRACHECKS
|
||||
case SCST_CMD_STATE_PRE_XMIT_RESP:
|
||||
case SCST_CMD_STATE_DEV_PARSE:
|
||||
case SCST_CMD_STATE_PRE_PARSE:
|
||||
@@ -2792,16 +2793,18 @@ static int scst_dev_done(struct scst_cmd *cmd)
|
||||
case SCST_CMD_STATE_XMIT_RESP:
|
||||
case SCST_CMD_STATE_FINISHED:
|
||||
case SCST_CMD_STATE_FINISHED_INTERNAL:
|
||||
#else
|
||||
default:
|
||||
#endif
|
||||
cmd->state = state;
|
||||
break;
|
||||
|
||||
case SCST_CMD_STATE_NEED_THREAD_CTX:
|
||||
TRACE_DBG("Dev handler %s dev_done() requested "
|
||||
"thread context, rescheduling",
|
||||
dev->handler->name);
|
||||
res = SCST_CMD_STATE_RES_NEED_THREAD;
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_SCST_EXTRACHECKS
|
||||
default:
|
||||
if (state >= 0) {
|
||||
PRINT_ERROR("Dev handler %s dev_done() returned "
|
||||
@@ -2816,6 +2819,7 @@ static int scst_dev_done(struct scst_cmd *cmd)
|
||||
SCST_LOAD_SENSE(scst_sense_hardw_error));
|
||||
scst_set_cmd_abnormal_done_state(cmd);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (cmd->needs_unblocking)
|
||||
@@ -2838,7 +2842,6 @@ out:
|
||||
static int scst_pre_xmit_response(struct scst_cmd *cmd)
|
||||
{
|
||||
int res;
|
||||
struct scst_session *sess = cmd->sess;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
@@ -2860,6 +2863,10 @@ static int scst_pre_xmit_response(struct scst_cmd *cmd)
|
||||
#endif
|
||||
|
||||
if (likely(cmd->tgt_dev != NULL)) {
|
||||
/*
|
||||
* Those counters protect from not getting too long processing
|
||||
* latency, so we should decrement them after cmd completed.
|
||||
*/
|
||||
atomic_dec(&cmd->tgt_dev->tgt_dev_cmd_count);
|
||||
atomic_dec(&cmd->dev->dev_cmd_count);
|
||||
#ifdef CONFIG_SCST_ORDERED_READS
|
||||
@@ -2879,18 +2886,6 @@ static int scst_pre_xmit_response(struct scst_cmd *cmd)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we don't remove cmd from the search list here, before
|
||||
* submitting it for transmittion, we will have a race, when for
|
||||
* some reason cmd's release is delayed after transmittion and
|
||||
* initiator sends cmd with the same tag => it is possible that
|
||||
* a wrong cmd will be found by find() functions.
|
||||
*/
|
||||
spin_lock_irq(&sess->sess_list_lock);
|
||||
list_move_tail(&cmd->sess_cmd_list_entry,
|
||||
&sess->after_pre_xmit_cmd_list);
|
||||
spin_unlock_irq(&sess->sess_list_lock);
|
||||
|
||||
cmd->done = 1;
|
||||
smp_mb(); /* to sync with scst_abort_cmd() */
|
||||
|
||||
@@ -3318,7 +3313,7 @@ static int __scst_init_cmd(struct scst_cmd *cmd)
|
||||
|
||||
cnt = atomic_inc_return(&cmd->tgt_dev->tgt_dev_cmd_count);
|
||||
if (unlikely(cnt > SCST_MAX_TGT_DEV_COMMANDS)) {
|
||||
TRACE(TRACE_MGMT_MINOR,
|
||||
TRACE(TRACE_FLOW_CONTROL,
|
||||
"Too many pending commands (%d) in "
|
||||
"session, returning BUSY to initiator \"%s\"",
|
||||
cnt, (cmd->sess->initiator_name[0] == '\0') ?
|
||||
@@ -3329,7 +3324,7 @@ static int __scst_init_cmd(struct scst_cmd *cmd)
|
||||
cnt = atomic_inc_return(&cmd->dev->dev_cmd_count);
|
||||
if (unlikely(cnt > SCST_MAX_DEV_COMMANDS)) {
|
||||
if (!failure) {
|
||||
TRACE(TRACE_MGMT_MINOR,
|
||||
TRACE(TRACE_FLOW_CONTROL,
|
||||
"Too many pending device "
|
||||
"commands (%d), returning BUSY to "
|
||||
"initiator \"%s\"", cnt,
|
||||
@@ -3538,6 +3533,10 @@ void scst_process_active_cmd(struct scst_cmd *cmd, bool atomic)
|
||||
res = scst_prepare_space(cmd);
|
||||
break;
|
||||
|
||||
case SCST_CMD_STATE_PREPROCESSING_DONE:
|
||||
res = scst_preprocessing_done(cmd);
|
||||
break;
|
||||
|
||||
case SCST_CMD_STATE_RDY_TO_XFER:
|
||||
res = scst_rdy_to_xfer(cmd);
|
||||
break;
|
||||
@@ -3624,8 +3623,8 @@ void scst_process_active_cmd(struct scst_cmd *cmd, bool atomic)
|
||||
/* None */
|
||||
} else if (res == SCST_CMD_STATE_RES_NEED_THREAD) {
|
||||
spin_lock_irq(&cmd->cmd_lists->cmd_list_lock);
|
||||
#ifdef CONFIG_SCST_EXTRACHECKS
|
||||
switch (cmd->state) {
|
||||
case SCST_CMD_STATE_PRE_PARSE:
|
||||
case SCST_CMD_STATE_DEV_PARSE:
|
||||
case SCST_CMD_STATE_PREPARE_SPACE:
|
||||
case SCST_CMD_STATE_RDY_TO_XFER:
|
||||
@@ -3633,32 +3632,24 @@ void scst_process_active_cmd(struct scst_cmd *cmd, bool atomic)
|
||||
case SCST_CMD_STATE_SEND_FOR_EXEC:
|
||||
case SCST_CMD_STATE_LOCAL_EXEC:
|
||||
case SCST_CMD_STATE_REAL_EXEC:
|
||||
case SCST_CMD_STATE_PRE_DEV_DONE:
|
||||
case SCST_CMD_STATE_MODE_SELECT_CHECKS:
|
||||
case SCST_CMD_STATE_DEV_DONE:
|
||||
case SCST_CMD_STATE_PRE_XMIT_RESP:
|
||||
case SCST_CMD_STATE_XMIT_RESP:
|
||||
case SCST_CMD_STATE_FINISHED:
|
||||
case SCST_CMD_STATE_FINISHED_INTERNAL:
|
||||
#endif
|
||||
TRACE_DBG("Adding cmd %p to head of active cmd list",
|
||||
cmd);
|
||||
list_add(&cmd->cmd_list_entry,
|
||||
&cmd->cmd_lists->active_cmd_list);
|
||||
break;
|
||||
#ifdef CONFIG_SCST_EXTRACHECKS
|
||||
/* not very valid commands */
|
||||
case SCST_CMD_STATE_DEFAULT:
|
||||
case SCST_CMD_STATE_NEED_THREAD_CTX:
|
||||
break;
|
||||
default:
|
||||
PRINT_CRIT_ERROR("cmd %p is in invalid state %d)", cmd,
|
||||
cmd->state);
|
||||
spin_unlock_irq(&cmd->cmd_lists->cmd_list_lock);
|
||||
sBUG();
|
||||
spin_lock_irq(&cmd->cmd_lists->cmd_list_lock);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
wake_up(&cmd->cmd_lists->cmd_list_waitQ);
|
||||
spin_unlock_irq(&cmd->cmd_lists->cmd_list_lock);
|
||||
} else
|
||||
@@ -4097,7 +4088,7 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
|
||||
|
||||
/*
|
||||
* cmd can't die here or sess_list_lock already taken and
|
||||
* cmd is in the search list
|
||||
* cmd is in the sess list
|
||||
*/
|
||||
list_add_tail(&mstb->cmd_mgmt_cmd_list_entry,
|
||||
&cmd->mgmt_cmd_list);
|
||||
@@ -4250,8 +4241,8 @@ static void __scst_abort_task_set(struct scst_mgmt_cmd *mcmd,
|
||||
|
||||
spin_lock_irq(&sess->sess_list_lock);
|
||||
|
||||
TRACE_DBG("Searching in search cmd list (sess=%p)", sess);
|
||||
list_for_each_entry(cmd, &sess->search_cmd_list,
|
||||
TRACE_DBG("Searching in sess cmd list (sess=%p)", sess);
|
||||
list_for_each_entry(cmd, &sess->sess_cmd_list,
|
||||
sess_cmd_list_entry) {
|
||||
if ((cmd->tgt_dev == tgt_dev) ||
|
||||
((cmd->tgt_dev == NULL) &&
|
||||
@@ -4361,8 +4352,8 @@ static int scst_clear_task_set(struct scst_mgmt_cmd *mcmd)
|
||||
|
||||
spin_lock_irq(&sess->sess_list_lock);
|
||||
|
||||
TRACE_DBG("Searching in search cmd list (sess=%p)", sess);
|
||||
list_for_each_entry(cmd, &sess->search_cmd_list,
|
||||
TRACE_DBG("Searching in sess cmd list (sess=%p)", sess);
|
||||
list_for_each_entry(cmd, &sess->sess_cmd_list,
|
||||
sess_cmd_list_entry) {
|
||||
if ((cmd->dev == dev) ||
|
||||
((cmd->dev == NULL) &&
|
||||
@@ -5698,10 +5689,19 @@ static struct scst_cmd *__scst_find_cmd_by_tag(struct scst_session *sess,
|
||||
|
||||
/* ToDo: hash list */
|
||||
|
||||
TRACE_DBG("%s (sess=%p, tag=%llu)", "Searching in search cmd list",
|
||||
TRACE_DBG("%s (sess=%p, tag=%llu)", "Searching in sess cmd list",
|
||||
sess, (long long unsigned int)tag);
|
||||
list_for_each_entry(cmd, &sess->search_cmd_list,
|
||||
list_for_each_entry(cmd, &sess->sess_cmd_list,
|
||||
sess_cmd_list_entry) {
|
||||
/*
|
||||
* We must not count done commands, because they were
|
||||
* submitted for transmittion. Otherwise we can have a race,
|
||||
* when for some reason cmd's release delayed after
|
||||
* transmittion and initiator sends cmd with the same tag =>
|
||||
* it can be possible that a wrong cmd will be returned.
|
||||
*/
|
||||
if (cmd->done)
|
||||
continue;
|
||||
if (cmd->tag == tag)
|
||||
goto out;
|
||||
}
|
||||
@@ -5725,9 +5725,17 @@ struct scst_cmd *scst_find_cmd(struct scst_session *sess, void *data,
|
||||
|
||||
spin_lock_irqsave(&sess->sess_list_lock, flags);
|
||||
|
||||
TRACE_DBG("Searching in search cmd list (sess=%p)", sess);
|
||||
list_for_each_entry(cmd, &sess->search_cmd_list,
|
||||
sess_cmd_list_entry) {
|
||||
TRACE_DBG("Searching in sess cmd list (sess=%p)", sess);
|
||||
list_for_each_entry(cmd, &sess->sess_cmd_list, sess_cmd_list_entry) {
|
||||
/*
|
||||
* We must not count done commands, because they were
|
||||
* submitted for transmittion. Otherwise we can have a race,
|
||||
* when for some reason cmd's release delayed after
|
||||
* transmittion and initiator sends cmd with the same tag =>
|
||||
* it can be possible that a wrong cmd will be returned.
|
||||
*/
|
||||
if (cmd->done)
|
||||
continue;
|
||||
if (cmp_fn(cmd, data))
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user