mirror of
https://github.com/SCST-project/scst.git
synced 2026-05-28 09:20:18 +00:00
A bunch of pending changes:
- Response data send timeout implemented: now if initiator doesn't accept data for too long (7 sec), target closes connection - Connction closing improved: now session cleared without need to wait for all outstanding commands data fully transmitted - On session unregistration now all outstanding commands are implicitely aborted - TM processing in SCST core made independant from other TM commands (before they were serialized) - Few bug fixes - Other minor improvements and cleanups - Docs update git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@295 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
#include <sys/uio.h>
|
||||
#endif
|
||||
|
||||
#define ISCSI_VERSION_STRING "0.9.6/0.4.15r147"
|
||||
#define ISCSI_VERSION_STRING "0.9.6/0.4.15r148"
|
||||
|
||||
/* The maximum length of 223 bytes in the RFC. */
|
||||
#define ISCSI_NAME_LEN 256
|
||||
|
||||
@@ -158,15 +158,21 @@ void iscsi_make_conn_wr_active(struct iscsi_conn *conn)
|
||||
return;
|
||||
}
|
||||
|
||||
void mark_conn_closed(struct iscsi_conn *conn)
|
||||
void __mark_conn_closed(struct iscsi_conn *conn, bool force)
|
||||
{
|
||||
spin_lock_bh(&iscsi_rd_lock);
|
||||
conn->closing = 1;
|
||||
conn->force_close = force;
|
||||
spin_unlock_bh(&iscsi_rd_lock);
|
||||
|
||||
iscsi_make_conn_rd_active(conn);
|
||||
}
|
||||
|
||||
void mark_conn_closed(struct iscsi_conn *conn)
|
||||
{
|
||||
__mark_conn_closed(conn, 0);
|
||||
}
|
||||
|
||||
static void iscsi_state_change(struct sock *sk)
|
||||
{
|
||||
struct iscsi_conn *conn = sk->sk_user_data;
|
||||
@@ -178,7 +184,7 @@ static void iscsi_state_change(struct sock *sk)
|
||||
PRINT_ERROR("Connection with initiator %s (%p) "
|
||||
"unexpectedly closed!",
|
||||
conn->session->initiator_name, conn);
|
||||
mark_conn_closed(conn);
|
||||
__mark_conn_closed(conn, 1);
|
||||
}
|
||||
} else
|
||||
iscsi_make_conn_rd_active(conn);
|
||||
@@ -226,6 +232,45 @@ static void iscsi_write_space_ready(struct sock *sk)
|
||||
return;
|
||||
}
|
||||
|
||||
static void conn_rsp_timer_fn(unsigned long arg)
|
||||
{
|
||||
struct iscsi_conn *conn = (struct iscsi_conn *)arg;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_DBG("Timer (conn %p)", conn);
|
||||
|
||||
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, write_list_entry);
|
||||
|
||||
if (unlikely(time_after_eq(jiffies, wr_cmd->write_timeout))) {
|
||||
if (!conn->closing) {
|
||||
PRINT_ERROR("Timeout sending data to initiator "
|
||||
"%s (SID %Lx), closing connection",
|
||||
conn->session->initiator_name,
|
||||
conn->session->sid);
|
||||
__mark_conn_closed(conn, 1);
|
||||
}
|
||||
} else {
|
||||
TRACE_DBG("Restarting timer on %ld (conn %p)",
|
||||
wr_cmd->write_timeout, conn);
|
||||
/*
|
||||
* Timer might have been restarted while we were
|
||||
* entering here.
|
||||
*/
|
||||
mod_timer(&conn->rsp_timer, wr_cmd->write_timeout);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_bh(&conn->write_list_lock);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
static int iscsi_socket_bind(struct iscsi_conn *conn)
|
||||
{
|
||||
int res = 0;
|
||||
@@ -279,6 +324,8 @@ int conn_free(struct iscsi_conn *conn)
|
||||
conn->session, (unsigned long long)conn->session->sid,
|
||||
conn->cid);
|
||||
|
||||
del_timer_sync(&conn->rsp_timer);
|
||||
|
||||
sBUG_ON(atomic_read(&conn->conn_ref_cnt) != 0);
|
||||
sBUG_ON(!list_empty(&conn->cmd_list));
|
||||
sBUG_ON(!list_empty(&conn->write_list));
|
||||
@@ -337,6 +384,8 @@ static int iscsi_conn_alloc(struct iscsi_session *session, struct conn_info *inf
|
||||
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);
|
||||
setup_timer(&conn->rsp_timer, conn_rsp_timer_fn, (unsigned long)conn);
|
||||
|
||||
conn->file = fget(info->fd);
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ void cmnd_free(struct iscsi_cmnd *cmnd)
|
||||
|
||||
kfree(cmnd->pdu.ahs);
|
||||
|
||||
if (unlikely(cmnd->on_write_list)) {
|
||||
if (unlikely(cmnd->on_write_list || cmnd->on_written_list)) {
|
||||
struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd);
|
||||
|
||||
PRINT_ERROR("cmnd %p still on some list?, %x, %x, %x, %x, %x, %x, %x",
|
||||
@@ -211,6 +211,16 @@ void cmnd_done(struct iscsi_cmnd *cmnd)
|
||||
|
||||
EXTRACHECKS_BUG_ON(cmnd->on_rx_digest_list);
|
||||
|
||||
if (cmnd->on_written_list) {
|
||||
struct iscsi_conn *conn = cmnd->conn;
|
||||
TRACE_DBG("Deleting cmd %p from conn %p written_list", cmnd,
|
||||
conn);
|
||||
spin_lock_bh(&conn->write_list_lock);
|
||||
list_del(&cmnd->write_list_entry);
|
||||
cmnd->on_written_list = 0;
|
||||
spin_unlock_bh(&conn->write_list_lock);
|
||||
}
|
||||
|
||||
if (cmnd->parent_req == NULL) {
|
||||
struct iscsi_conn *conn = cmnd->conn;
|
||||
TRACE_DBG("Deleting req %p from conn %p", cmnd, conn);
|
||||
@@ -305,7 +315,7 @@ void req_cmnd_release_force(struct iscsi_cmnd *req, int flags)
|
||||
sBUG_ON(req == conn->read_cmnd);
|
||||
|
||||
if (flags & ISCSI_FORCE_RELEASE_WRITE) {
|
||||
spin_lock(&conn->write_list_lock);
|
||||
spin_lock_bh(&conn->write_list_lock);
|
||||
list_for_each_entry_safe(rsp, t, &conn->write_list,
|
||||
write_list_entry) {
|
||||
if (rsp->parent_req != req)
|
||||
@@ -315,7 +325,7 @@ void req_cmnd_release_force(struct iscsi_cmnd *req, int flags)
|
||||
|
||||
list_add_tail(&rsp->write_list_entry, &cmds_list);
|
||||
}
|
||||
spin_unlock(&conn->write_list_lock);
|
||||
spin_unlock_bh(&conn->write_list_lock);
|
||||
|
||||
list_for_each_entry_safe(rsp, t, &cmds_list, write_list_entry) {
|
||||
list_del(&rsp->write_list_entry);
|
||||
@@ -338,18 +348,18 @@ again_rsp:
|
||||
|
||||
spin_unlock_bh(&req->rsp_cmd_lock);
|
||||
|
||||
spin_lock(&conn->write_list_lock);
|
||||
spin_lock_bh(&conn->write_list_lock);
|
||||
r = rsp->on_write_list || rsp->write_processing_started;
|
||||
spin_unlock(&conn->write_list_lock);
|
||||
spin_unlock_bh(&conn->write_list_lock);
|
||||
|
||||
cmnd_put(rsp);
|
||||
|
||||
if (r)
|
||||
continue;
|
||||
goto again_rsp;
|
||||
|
||||
/*
|
||||
* If both on_write_list and write_processing_started not set,
|
||||
* we can safely put() cmnd
|
||||
* we can safely put() rsp.
|
||||
*/
|
||||
cmnd_put(rsp);
|
||||
goto again_rsp;
|
||||
@@ -488,6 +498,20 @@ static void iscsi_cmnds_init_write(struct list_head *send, int flags)
|
||||
(rsp->parent_req->outstanding_r2t == 0))
|
||||
cmnd_remove_hash(rsp->parent_req);
|
||||
|
||||
if (!(conn->ddigest_type & DIGEST_NONE)) {
|
||||
list_for_each(pos, send) {
|
||||
rsp = list_entry(pos, struct iscsi_cmnd,
|
||||
write_list_entry);
|
||||
|
||||
if (rsp->pdu.datasize != 0) {
|
||||
TRACE_DBG("Doing data digest (%p:%x)", rsp,
|
||||
cmnd_opcode(rsp));
|
||||
digest_tx_data(rsp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock_bh(&conn->write_list_lock);
|
||||
list_for_each_safe(pos, next, send) {
|
||||
rsp = list_entry(pos, struct iscsi_cmnd, write_list_entry);
|
||||
|
||||
@@ -495,16 +519,10 @@ static void iscsi_cmnds_init_write(struct list_head *send, int flags)
|
||||
|
||||
sBUG_ON(conn != rsp->conn);
|
||||
|
||||
if (!(conn->ddigest_type & DIGEST_NONE) &&
|
||||
(rsp->pdu.datasize != 0))
|
||||
digest_tx_data(rsp);
|
||||
|
||||
list_del(&rsp->write_list_entry);
|
||||
|
||||
spin_lock(&conn->write_list_lock);
|
||||
cmd_add_on_write_list(conn, rsp);
|
||||
spin_unlock(&conn->write_list_lock);
|
||||
}
|
||||
spin_unlock_bh(&conn->write_list_lock);
|
||||
|
||||
if (flags & ISCSI_INIT_WRITE_WAKE)
|
||||
iscsi_make_conn_wr_active(conn);
|
||||
@@ -1090,6 +1108,17 @@ static int iscsi_pre_exec(struct scst_cmd *scst_cmd)
|
||||
|
||||
EXTRACHECKS_BUG_ON(scst_cmd_atomic(scst_cmd));
|
||||
|
||||
if (scst_cmd_get_data_direction(scst_cmd) == SCST_DATA_READ) {
|
||||
if (!(req->conn->ddigest_type & DIGEST_NONE))
|
||||
scst_set_long_xmit(scst_cmd);
|
||||
#ifndef NET_PAGE_CALLBACKS_DEFINED
|
||||
else if (cmnd_hdr(req)->data_length > 8*1024)
|
||||
scst_set_long_xmit(scst_cmd);
|
||||
#endif
|
||||
EXTRACHECKS_BUG_ON(!list_empty(&req->rx_ddigest_cmd_list));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If data digest isn't used this list will be empty */
|
||||
list_for_each_entry_safe(c, t, &req->rx_ddigest_cmd_list,
|
||||
rx_ddigest_cmd_list_entry) {
|
||||
@@ -2366,7 +2395,8 @@ static void iscsi_preprocessing_done(struct scst_cmd *scst_cmd)
|
||||
* upon entrance in this function, because otherwise it could be destroyed
|
||||
* inside as a result of iscsi_send(), which releases sent commands.
|
||||
*/
|
||||
static void iscsi_try_local_processing(struct iscsi_conn *conn)
|
||||
static void iscsi_try_local_processing(struct iscsi_conn *conn,
|
||||
bool single_only)
|
||||
{
|
||||
int local;
|
||||
|
||||
@@ -2395,7 +2425,7 @@ static void iscsi_try_local_processing(struct iscsi_conn *conn)
|
||||
int rc = 1;
|
||||
while(test_write_ready(conn)) {
|
||||
rc = iscsi_send(conn);
|
||||
if (rc <= 0) {
|
||||
if ((rc <= 0) || single_only) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2427,6 +2457,7 @@ static int iscsi_xmit_response(struct scst_cmd *scst_cmd)
|
||||
u8 *sense = scst_cmd_get_sense_buffer(scst_cmd);
|
||||
int sense_len = scst_cmd_get_sense_buffer_len(scst_cmd);
|
||||
int old_state = req->scst_state;
|
||||
bool single_only = !scst_get_long_xmit(scst_cmd);
|
||||
|
||||
scst_cmd_set_tgt_priv(scst_cmd, NULL);
|
||||
|
||||
@@ -2525,7 +2556,7 @@ static int iscsi_xmit_response(struct scst_cmd *scst_cmd)
|
||||
|
||||
conn_get_ordered(conn);
|
||||
req_cmnd_release(req);
|
||||
iscsi_try_local_processing(conn);
|
||||
iscsi_try_local_processing(conn, single_only);
|
||||
conn_put(conn);
|
||||
|
||||
out:
|
||||
@@ -2667,12 +2698,15 @@ static void iscsi_task_mgmt_fn_done(struct scst_mgmt_cmd *scst_mcmd)
|
||||
scst_mgmt_cmd_get_tgt_priv(scst_mcmd);
|
||||
int status = iscsi_get_mgmt_response(scst_mgmt_cmd_get_status(scst_mcmd));
|
||||
|
||||
TRACE_MGMT_DBG("req %p, scst_mcmd %p, scst status %d", req, scst_mcmd,
|
||||
TRACE_MGMT_DBG("req %p, scst_mcmd %p, fn %d, scst status %d",
|
||||
req, scst_mcmd, scst_mgmt_cmd_get_fn(scst_mcmd),
|
||||
scst_mgmt_cmd_get_status(scst_mcmd));
|
||||
|
||||
iscsi_send_task_mgmt_resp(req, status);
|
||||
|
||||
scst_mgmt_cmd_set_tgt_priv(scst_mcmd, NULL);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int iscsi_target_detect(struct scst_tgt_template *templ)
|
||||
|
||||
@@ -100,6 +100,7 @@ struct iscsi_session {
|
||||
|
||||
/* All 3 protected by sn_lock */
|
||||
unsigned int tm_active:1;
|
||||
unsigned int shutting_down:1; /* Let's save some cache footprint by putting it here */
|
||||
u32 tm_sn;
|
||||
struct iscsi_cmnd *tm_rsp;
|
||||
|
||||
@@ -113,6 +114,8 @@ struct iscsi_session {
|
||||
|
||||
struct list_head session_list_entry;
|
||||
|
||||
struct completion unreg_compl;
|
||||
|
||||
/* Both don't need any protection */
|
||||
char *initiator_name;
|
||||
u64 sid;
|
||||
@@ -145,7 +148,11 @@ struct iscsi_conn {
|
||||
|
||||
spinlock_t write_list_lock;
|
||||
/* List of data pdus to be sent, protected by write_list_lock */
|
||||
struct list_head write_list;
|
||||
struct list_head write_list;
|
||||
/* List of data pdus being sent, protected by write_list_lock */
|
||||
struct list_head written_list;
|
||||
|
||||
struct timer_list rsp_timer;
|
||||
|
||||
/* All 2 protected by iscsi_wr_lock */
|
||||
unsigned short wr_state;
|
||||
@@ -182,6 +189,7 @@ struct iscsi_conn {
|
||||
unsigned short rd_state;
|
||||
unsigned short rd_data_ready:1;
|
||||
unsigned short closing:1; /* Let's save some cache footprint by putting it here */
|
||||
unsigned short force_close:1; /* Let's save some cache footprint by putting it here */
|
||||
|
||||
struct list_head rd_list_entry;
|
||||
|
||||
@@ -238,6 +246,7 @@ struct iscsi_cmnd {
|
||||
unsigned int force_cleanup_done:1;
|
||||
unsigned int dec_active_cmnds:1;
|
||||
unsigned int ddigest_checked:1;
|
||||
unsigned int on_written_list:1;
|
||||
#ifdef EXTRACHECKS
|
||||
unsigned int on_rx_digest_list:1;
|
||||
unsigned int release_called:1;
|
||||
@@ -262,6 +271,8 @@ struct iscsi_cmnd {
|
||||
struct list_head write_list_entry;
|
||||
};
|
||||
|
||||
unsigned long write_timeout;
|
||||
|
||||
/*
|
||||
* Unprotected, since could be accessed from only a single
|
||||
* thread at time
|
||||
@@ -306,6 +317,8 @@ struct iscsi_cmnd {
|
||||
/* Flags for req_cmnd_release_force() */
|
||||
#define ISCSI_FORCE_RELEASE_WRITE 1
|
||||
|
||||
#define ISCSI_RSP_TIMEOUT (7*HZ)
|
||||
|
||||
extern struct mutex target_mgmt_mutex;
|
||||
|
||||
extern struct file_operations ctr_fops;
|
||||
@@ -336,6 +349,7 @@ extern struct iscsi_conn *conn_lookup(struct iscsi_session *, u16);
|
||||
extern int conn_add(struct iscsi_session *, struct conn_info *);
|
||||
extern int conn_del(struct iscsi_session *, struct conn_info *);
|
||||
extern int conn_free(struct iscsi_conn *);
|
||||
extern void __mark_conn_closed(struct iscsi_conn *, bool);
|
||||
extern void mark_conn_closed(struct iscsi_conn *);
|
||||
extern void iscsi_make_conn_wr_active(struct iscsi_conn *);
|
||||
extern void conn_info_show(struct seq_file *, struct iscsi_session *);
|
||||
@@ -447,7 +461,7 @@ static inline void cmnd_put(struct iscsi_cmnd *cmnd)
|
||||
cmnd_done(cmnd);
|
||||
}
|
||||
|
||||
/* conn->write_list_lock supposed to be locked */
|
||||
/* conn->write_list_lock supposed to be locked and BHs off */
|
||||
static inline void cmd_add_on_write_list(struct iscsi_conn *conn,
|
||||
struct iscsi_cmnd *cmnd)
|
||||
{
|
||||
@@ -456,7 +470,7 @@ static inline void cmd_add_on_write_list(struct iscsi_conn *conn,
|
||||
cmnd->on_write_list = 1;
|
||||
}
|
||||
|
||||
/* conn->write_list_lock supposed to be locked */
|
||||
/* conn->write_list_lock supposed to be locked and BHs off */
|
||||
static inline void cmd_del_from_write_list(struct iscsi_cmnd *cmnd)
|
||||
{
|
||||
TRACE_DBG("%p", cmnd);
|
||||
|
||||
@@ -165,6 +165,22 @@ out:
|
||||
static inline void iscsi_check_closewait(struct iscsi_conn *conn) {};
|
||||
#endif
|
||||
|
||||
static void iscsi_unreg_cmds_done_fn(struct scst_session *scst_sess)
|
||||
{
|
||||
struct iscsi_session *sess =
|
||||
(struct iscsi_session *)scst_sess_get_tgt_priv(scst_sess);
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_CONN_CLOSE("sess %p (scst_sess %p)", sess, scst_sess);
|
||||
|
||||
sess->shutting_down = 1;
|
||||
complete_all(&sess->unreg_compl);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
/* No locks */
|
||||
static void close_conn(struct iscsi_conn *conn)
|
||||
{
|
||||
@@ -181,8 +197,13 @@ static void close_conn(struct iscsi_conn *conn)
|
||||
|
||||
sBUG_ON(!conn->closing);
|
||||
|
||||
/* We want all our already send operations to complete */
|
||||
conn->sock->ops->shutdown(conn->sock, RCV_SHUTDOWN);
|
||||
if (conn->force_close) {
|
||||
conn->sock->ops->shutdown(conn->sock,
|
||||
RCV_SHUTDOWN|SEND_SHUTDOWN);
|
||||
} else {
|
||||
/* We want all our already send operations to complete */
|
||||
conn->sock->ops->shutdown(conn->sock, RCV_SHUTDOWN);
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to call scst_unregister_session() ASAP to make SCST start
|
||||
@@ -190,7 +211,8 @@ static void close_conn(struct iscsi_conn *conn)
|
||||
*
|
||||
* ToDo: this is incompatible with MC/S
|
||||
*/
|
||||
scst_unregister_session(session->scst_sess, 0, NULL);
|
||||
scst_unregister_session_ex(session->scst_sess, 0,
|
||||
NULL, iscsi_unreg_cmds_done_fn);
|
||||
session->scst_sess = NULL;
|
||||
|
||||
if (conn->read_state != RX_INIT_BHS) {
|
||||
@@ -269,8 +291,8 @@ static void close_conn(struct iscsi_conn *conn)
|
||||
} while(req_freed);
|
||||
spin_unlock(&session->sn_lock);
|
||||
|
||||
if (time_after(jiffies, start_waiting + 5*HZ)) {
|
||||
TRACE_CONN_CLOSE("%s", "Wait time expired");
|
||||
if (time_after(jiffies, start_waiting + 10*HZ)) {
|
||||
TRACE_CONN_CLOSE("%s", "Pending wait time expired");
|
||||
spin_lock(&session->sn_lock);
|
||||
do {
|
||||
req_freed = 0;
|
||||
@@ -305,6 +327,12 @@ static void close_conn(struct iscsi_conn *conn)
|
||||
}
|
||||
|
||||
iscsi_make_conn_wr_active(conn);
|
||||
|
||||
if (time_after(jiffies, start_waiting + 7*HZ)) {
|
||||
TRACE_CONN_CLOSE("%s", "Wait time expired");
|
||||
conn->sock->ops->shutdown(conn->sock, SEND_SHUTDOWN);
|
||||
}
|
||||
|
||||
msleep(200);
|
||||
|
||||
TRACE_CONN_CLOSE("conn %p, conn_ref_cnt %d left, wr_state %d, "
|
||||
@@ -388,6 +416,8 @@ static void close_conn(struct iscsi_conn *conn)
|
||||
TRACE_CONN_CLOSE("Notifying user space about closing connection %p", conn);
|
||||
event_send(target->tid, session->sid, conn->cid, E_CONN_CLOSE, 0);
|
||||
|
||||
wait_for_completion(&session->unreg_compl);
|
||||
|
||||
mutex_lock(&target->target_mutex);
|
||||
conn_free(conn);
|
||||
/* ToDo: this is incompatible with MC/S */
|
||||
@@ -453,14 +483,14 @@ static struct iscsi_cmnd *iscsi_get_send_cmnd(struct iscsi_conn *conn)
|
||||
{
|
||||
struct iscsi_cmnd *cmnd = NULL;
|
||||
|
||||
spin_lock(&conn->write_list_lock);
|
||||
spin_lock_bh(&conn->write_list_lock);
|
||||
if (!list_empty(&conn->write_list)) {
|
||||
cmnd = list_entry(conn->write_list.next, struct iscsi_cmnd,
|
||||
write_list_entry);
|
||||
cmd_del_from_write_list(cmnd);
|
||||
cmnd->write_processing_started = 1;
|
||||
}
|
||||
spin_unlock(&conn->write_list_lock);
|
||||
spin_unlock_bh(&conn->write_list_lock);
|
||||
|
||||
return cmnd;
|
||||
}
|
||||
@@ -862,6 +892,28 @@ static int write_data(struct iscsi_conn *conn)
|
||||
else
|
||||
ref_cmd = write_cmnd;
|
||||
|
||||
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->write_list_entry, &conn->written_list);
|
||||
spin_unlock_bh(&conn->write_list_lock);
|
||||
}
|
||||
|
||||
if (!timer_pending(&conn->rsp_timer)) {
|
||||
sBUG_ON(!ref_cmd->write_timeout);
|
||||
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);
|
||||
}
|
||||
|
||||
file = conn->file;
|
||||
saved_size = size = conn->write_size;
|
||||
iop = conn->write_iop;
|
||||
|
||||
@@ -22,7 +22,7 @@ struct iscsi_session *session_lookup(struct iscsi_target *target, u64 sid)
|
||||
struct iscsi_session *session;
|
||||
|
||||
list_for_each_entry(session, &target->session_list, session_list_entry) {
|
||||
if (session->sid == sid)
|
||||
if ((session->sid == sid) && !session->shutting_down)
|
||||
return session;
|
||||
}
|
||||
return NULL;
|
||||
@@ -35,9 +35,6 @@ static int iscsi_session_alloc(struct iscsi_target *target, struct session_info
|
||||
struct iscsi_session *session;
|
||||
char *name = NULL;
|
||||
|
||||
TRACE_MGMT_DBG("Creating session: target %p, tid %u, sid %#Lx",
|
||||
target, target->tid, (unsigned long long) info->sid);
|
||||
|
||||
if (!(session = kzalloc(sizeof(*session), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -91,8 +88,14 @@ static int iscsi_session_alloc(struct iscsi_target *target, struct session_info
|
||||
|
||||
kfree(name);
|
||||
|
||||
scst_sess_set_tgt_priv(session->scst_sess, session);
|
||||
init_completion(&session->unreg_compl);
|
||||
|
||||
list_add(&session->session_list_entry, &target->session_list);
|
||||
|
||||
TRACE_MGMT_DBG("Session %p created: target %p, tid %u, sid %#Lx",
|
||||
session, target, target->tid, (unsigned long long) info->sid);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
if (session) {
|
||||
@@ -110,8 +113,11 @@ int session_add(struct iscsi_target *target, struct session_info *info)
|
||||
int err = -EEXIST;
|
||||
|
||||
session = session_lookup(target, info->sid);
|
||||
if (session)
|
||||
if (session) {
|
||||
PRINT_ERROR("Attempt to add session with existing SID %Lx",
|
||||
info->sid);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = iscsi_session_alloc(target, info);
|
||||
|
||||
|
||||
@@ -264,6 +264,8 @@ void target_del_all(void)
|
||||
|
||||
mutex_unlock(&target_mgmt_mutex);
|
||||
|
||||
TRACE_MGMT_DBG("%s", "Deleting all targets finished");
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -471,8 +471,11 @@ subdirectories "vdisk" and "vcdrom". They have similar layout:
|
||||
|
||||
* "change NAME [PATH]" - changes a virtual CD in the VDISK CDROM.
|
||||
|
||||
By default, if neither BLOCKIO, nor NULLIO option is supplied, FILEIO
|
||||
mode is used.
|
||||
|
||||
For example, "echo "open disk1 /vdisks/disk1" >/proc/scsi_tgt/vdisk/vdisk"
|
||||
will open file /vdisks/disk1 as virtual VDISK disk with name "disk1".
|
||||
will open file /vdisks/disk1 as virtual FILEIO disk with name "disk1".
|
||||
|
||||
IMPORTANT: By default for performance reasons VDISK FILEIO devices use write
|
||||
========= back caching policy. This is generally safe from the consistence of
|
||||
|
||||
@@ -140,13 +140,16 @@ typedef _Bool bool;
|
||||
*************************************************************/
|
||||
|
||||
/* LUN translation (mcmd->tgt_dev assignment) */
|
||||
#define SCST_MGMT_CMD_STATE_INIT 1
|
||||
#define SCST_MGMT_CMD_STATE_INIT 0
|
||||
|
||||
/* Mgmt cmd is ready for processing */
|
||||
#define SCST_MGMT_CMD_STATE_READY 2
|
||||
#define SCST_MGMT_CMD_STATE_READY 1
|
||||
|
||||
/* Mgmt cmd is being executing */
|
||||
#define SCST_MGMT_CMD_STATE_EXECUTING 3
|
||||
#define SCST_MGMT_CMD_STATE_EXECUTING 2
|
||||
|
||||
/* Reservations are going to be cleared, if necessary */
|
||||
#define SCST_MGMT_CMD_STATE_CHECK_NEXUS_LOSS 3
|
||||
|
||||
/* Target driver's task_mgmt_fn_done() is going to be called */
|
||||
#define SCST_MGMT_CMD_STATE_DONE 4
|
||||
@@ -167,15 +170,15 @@ typedef _Bool bool;
|
||||
|
||||
/*
|
||||
* Direct cmd's processing (i.e. regular function calls in the current
|
||||
* context), sleeping is allowed, no restrictions
|
||||
* context) sleeping is not allowed
|
||||
*/
|
||||
#define SCST_CONTEXT_DIRECT 0
|
||||
#define SCST_CONTEXT_DIRECT_ATOMIC 0
|
||||
|
||||
/*
|
||||
* Direct cmd's processing (i.e. regular function calls in the current
|
||||
* context) sleeping is not allowed
|
||||
* context), sleeping is allowed, no restrictions
|
||||
*/
|
||||
#define SCST_CONTEXT_DIRECT_ATOMIC 1
|
||||
#define SCST_CONTEXT_DIRECT 1
|
||||
|
||||
/* Tasklet or thread context required for cmd's processing */
|
||||
#define SCST_CONTEXT_TASKLET 2
|
||||
@@ -183,6 +186,14 @@ typedef _Bool bool;
|
||||
/* Thread context required for cmd's processing */
|
||||
#define SCST_CONTEXT_THREAD 3
|
||||
|
||||
/*
|
||||
* SCST internal flag, which specifies that context is processable, i.e. the
|
||||
* next command in the active list will be processed after the current one.
|
||||
*
|
||||
* Target drivers must never use it!!
|
||||
*/
|
||||
#define SCST_CONTEXT_PROCESSABLE 0x100
|
||||
|
||||
/*************************************************************
|
||||
** Values for status parameter of scst_rx_data()
|
||||
*************************************************************/
|
||||
@@ -677,6 +688,9 @@ struct scst_tgt_template
|
||||
|
||||
struct scst_dev_type
|
||||
{
|
||||
/* SCSI type of the supported device. MUST HAVE */
|
||||
int type;
|
||||
|
||||
/*
|
||||
* True, if corresponding function supports execution in
|
||||
* the atomic (non-sleeping) context
|
||||
@@ -688,6 +702,9 @@ struct scst_dev_type
|
||||
/* Set, if no /proc files should be automatically created by SCST */
|
||||
unsigned no_proc:1;
|
||||
|
||||
/* Set, if exec() is synchronous */
|
||||
unsigned exec_sync:1;
|
||||
|
||||
/*
|
||||
* Called to parse CDB from the cmd and initialize
|
||||
* cmd->bufflen and cmd->data_direction (both - REQUIRED).
|
||||
@@ -721,6 +738,10 @@ struct scst_dev_type
|
||||
* by scst_cmd_atomic(): it is true if the function called in the
|
||||
* atomic (non-sleeping) context.
|
||||
*
|
||||
* If this function provides sync execution, you must set above
|
||||
* exec_sync flag and should consider to setup dedicated threads by
|
||||
* setting threads_num > 0.
|
||||
*
|
||||
* !! If this function is implemented, scst_check_local_events() shall !!
|
||||
* !! be called inside it just before the actual command's execution. !!
|
||||
*
|
||||
@@ -808,9 +829,6 @@ struct scst_dev_type
|
||||
/* Name of the dev handler. Must be unique. MUST HAVE */
|
||||
char name[15];
|
||||
|
||||
/* SCSI type of the supported device. MUST HAVE */
|
||||
int type;
|
||||
|
||||
/*
|
||||
* Number of dedicated threads. If 0 - no dedicated threads will
|
||||
* be created, if <0 - creation of dedicated threads is prohibited.
|
||||
@@ -951,8 +969,9 @@ struct scst_session
|
||||
void (*init_result_fn) (struct scst_session *sess, void *data,
|
||||
int result);
|
||||
void (*unreg_done_fn) (struct scst_session *sess);
|
||||
void (*unreg_cmds_done_fn) (struct scst_session *sess);
|
||||
|
||||
#ifdef MEASURE_LATENCY
|
||||
#ifdef MEASURE_LATENCY /* must be last */
|
||||
spinlock_t meas_lock;
|
||||
uint64_t scst_time, processing_time;
|
||||
unsigned int processed_cmds;
|
||||
@@ -1003,6 +1022,12 @@ struct scst_cmd
|
||||
/* Set if cmd is being processed in atomic context */
|
||||
unsigned int atomic:1;
|
||||
|
||||
/*
|
||||
* Set if the cmd is being processed in the processable context. See
|
||||
* comment for SCST_CONTEXT_PROCESSABLE for what it means.
|
||||
*/
|
||||
unsigned int context_processable:1;
|
||||
|
||||
/* Set if cmd is internally generated */
|
||||
unsigned int internal:1;
|
||||
|
||||
@@ -1085,18 +1110,25 @@ struct scst_cmd
|
||||
/* Set if the cmd was done or aborted out of its SN */
|
||||
unsigned int out_of_sn:1;
|
||||
|
||||
/* Set if the cmd is deferred HEAD OF QUEUE */
|
||||
unsigned int hq_deferred:1;
|
||||
/* Set if increment expected_sn in cmd->scst_cmd_done() */
|
||||
unsigned int inc_expected_sn_on_done:1;
|
||||
|
||||
/*
|
||||
* Set if increment expected_sn in cmd->scst_cmd_done() (to save
|
||||
* extra dereferences)
|
||||
* Set if xmit_response() is going to need a considerable processing
|
||||
* time. Processing time is considerable, if it's > context switch time
|
||||
* (about 1 usec on modern systems). It's needed to trigger other
|
||||
* threads to start processing other outstanding commands without
|
||||
* waiting XMIT for the current one to finish. E.g., it should be set
|
||||
* if iSCSI data digest used and cmd has READ direction.
|
||||
*/
|
||||
unsigned int inc_expected_sn_on_done:1;
|
||||
unsigned int long_xmit:1;
|
||||
|
||||
/* Set if tgt_sn field is valid */
|
||||
unsigned int tgt_sn_set:1;
|
||||
|
||||
/* Set if cmd is done */
|
||||
unsigned int done:1;
|
||||
|
||||
/* Set if cmd is finished */
|
||||
unsigned int finished:1;
|
||||
|
||||
@@ -1115,9 +1147,6 @@ struct scst_cmd
|
||||
|
||||
lun_t lun; /* LUN for this cmd */
|
||||
|
||||
/* The corresponding mgmt cmd, if any, protected by sess_list_lock */
|
||||
struct scst_mgmt_cmd *mgmt_cmnd;
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18)
|
||||
struct scsi_request *scsi_req; /* SCSI request */
|
||||
#endif
|
||||
@@ -1211,12 +1240,15 @@ struct scst_cmd
|
||||
*/
|
||||
int orig_sg_cnt, orig_sg_entry, orig_entry_len;
|
||||
|
||||
/* List corresponding mgmt cmd, if any, protected by sess_list_lock */
|
||||
struct list_head mgmt_cmd_list;
|
||||
|
||||
/* List entry for dev's blocked_cmd_list */
|
||||
struct list_head blocked_cmd_list_entry;
|
||||
|
||||
struct scst_cmd *orig_cmd; /* Used to issue REQUEST SENSE */
|
||||
|
||||
#ifdef MEASURE_LATENCY
|
||||
#ifdef MEASURE_LATENCY /* must be last */
|
||||
uint64_t start, pre_exec_finish, post_exec_start;
|
||||
#endif
|
||||
};
|
||||
@@ -1235,6 +1267,14 @@ struct scst_rx_mgmt_params
|
||||
unsigned char cmd_sn_set;
|
||||
};
|
||||
|
||||
struct scst_mgmt_cmd_stub
|
||||
{
|
||||
struct scst_mgmt_cmd *mcmd;
|
||||
|
||||
/* List entry in cmd->mgmt_cmd_list */
|
||||
struct list_head cmd_mgmt_cmd_list_entry;
|
||||
};
|
||||
|
||||
struct scst_mgmt_cmd
|
||||
{
|
||||
/* List entry for *_mgmt_cmd_list */
|
||||
@@ -1253,12 +1293,20 @@ struct scst_mgmt_cmd
|
||||
unsigned int needs_unblocking:1;
|
||||
unsigned int lun_set:1; /* set, if lun field is valid */
|
||||
unsigned int cmd_sn_set:1; /* set, if cmd_sn field is valid */
|
||||
unsigned int nexus_loss_check_active:1; /* set, if nexus loss check is active */
|
||||
unsigned int nexus_loss_check_done:1; /* set, if nexus loss check is done */
|
||||
|
||||
/*
|
||||
* Number of commands to complete before sending response,
|
||||
* Number of commands to finish before sending response,
|
||||
* protected by scst_mcmd_lock
|
||||
*/
|
||||
int cmd_wait_count;
|
||||
int cmd_finish_wait_count;
|
||||
|
||||
/*
|
||||
* Number of commands to complete (done) before resetting reservation,
|
||||
* protected by scst_mcmd_lock
|
||||
*/
|
||||
int cmd_done_wait_count;
|
||||
|
||||
/* Number of completed commands, protected by scst_mcmd_lock */
|
||||
int completed_cmd_count;
|
||||
@@ -1633,6 +1681,11 @@ struct scst_session *scst_register_session(struct scst_tgt *tgt, int atomic,
|
||||
* the session is about to be completely freed. Can be NULL.
|
||||
* Parameter:
|
||||
* - sess - session
|
||||
* unreg_cmds_done_fn - pointer to the function that will be
|
||||
* asynchronously called when the last session's command completes, i.e.
|
||||
* goes to XMIT stage. Can be NULL.
|
||||
* Parameter:
|
||||
* - sess - session
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
@@ -1652,8 +1705,15 @@ struct scst_session *scst_register_session(struct scst_tgt *tgt, int atomic,
|
||||
* but it also starts recovering stuck commands, if there are any.
|
||||
* Otherwise, your target driver could wait for those commands forever.
|
||||
*/
|
||||
void scst_unregister_session(struct scst_session *sess, int wait,
|
||||
void (*unreg_done_fn) (struct scst_session *sess));
|
||||
void scst_unregister_session_ex(struct scst_session *sess, int wait,
|
||||
void (*unreg_done_fn) (struct scst_session *sess),
|
||||
void (*unreg_cmds_done_fn) (struct scst_session *sess));
|
||||
|
||||
static inline void scst_unregister_session(struct scst_session *sess, int wait,
|
||||
void (*unreg_done_fn) (struct scst_session *sess))
|
||||
{
|
||||
scst_unregister_session_ex(sess, wait, unreg_done_fn, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Registers dev handler driver
|
||||
@@ -2220,6 +2280,24 @@ static inline void scst_set_delivery_status(struct scst_cmd *cmd,
|
||||
cmd->delivery_status = delivery_status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get/set/clear functions for cmd's long XMIT flag.
|
||||
*/
|
||||
static inline int scst_get_long_xmit(struct scst_cmd *cmd)
|
||||
{
|
||||
return cmd->long_xmit;
|
||||
}
|
||||
|
||||
static inline void scst_set_long_xmit(struct scst_cmd *cmd)
|
||||
{
|
||||
cmd->long_xmit = 1;
|
||||
}
|
||||
|
||||
static inline void scst_clear_long_xmit(struct scst_cmd *cmd)
|
||||
{
|
||||
cmd->long_xmit = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get/Set function for mgmt cmd's target private data
|
||||
*/
|
||||
@@ -2242,6 +2320,14 @@ static inline int scst_mgmt_cmd_get_status(struct scst_mgmt_cmd *mcmd)
|
||||
return mcmd->status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns mgmt cmd's TM fn
|
||||
*/
|
||||
static inline int scst_mgmt_cmd_get_fn(struct scst_mgmt_cmd *mcmd)
|
||||
{
|
||||
return mcmd->fn;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
|
||||
|
||||
static inline struct page *sg_page(struct scatterlist *sg)
|
||||
@@ -2390,7 +2476,8 @@ void scst_resume_activity(void);
|
||||
/*
|
||||
* Main SCST commands processing routing. Must be used only by dev handlers.
|
||||
* Argument context sets the execution context, only SCST_CONTEXT_DIRECT and
|
||||
* SCST_CONTEXT_DIRECT_ATOMIC are allowed.
|
||||
* SCST_CONTEXT_DIRECT_ATOMIC with optional SCST_CONTEXT_PROCESSABLE flag
|
||||
* are allowed.
|
||||
*/
|
||||
void scst_process_active_cmd(struct scst_cmd *cmd, int context);
|
||||
|
||||
|
||||
@@ -70,6 +70,14 @@
|
||||
/* Aborts all tasks in all sessions of the tgt */
|
||||
#define SCST_ABORT_ALL_TASKS 9
|
||||
|
||||
/*
|
||||
* Internal TM command issued by SCST in scst_unregister_session(). It is the
|
||||
* same as SCST_NEXUS_LOSS_SESS, except it calls unreg_cmds_done_fn().
|
||||
*
|
||||
* Target driver shall NEVER use it!!
|
||||
*/
|
||||
#define SCST_UNREG_SESS_TM 10
|
||||
|
||||
/*************************************************************
|
||||
** Values for mgmt cmd's status field. Codes taken from iSCSI
|
||||
*************************************************************/
|
||||
|
||||
@@ -93,7 +93,6 @@
|
||||
#ifdef DEBUG
|
||||
//# define LOG_FLAG KERN_DEBUG
|
||||
# define LOG_FLAG KERN_INFO
|
||||
|
||||
# define INFO_FLAG KERN_INFO
|
||||
# define ERROR_FLAG KERN_INFO
|
||||
#else
|
||||
@@ -102,6 +101,8 @@
|
||||
# define ERROR_FLAG KERN_ERR
|
||||
#endif
|
||||
|
||||
#define CRIT_FLAG KERN_CRIT
|
||||
|
||||
#define NO_FLAG ""
|
||||
|
||||
#define TRACE_NULL 0x00000000
|
||||
@@ -260,6 +261,16 @@ do { \
|
||||
PRINT_LOG_FLAG(ERROR_FLAG, "***ERROR*** " format, args); \
|
||||
} while(0)
|
||||
|
||||
#define PRINT_CRIT_ERROR(format, args...) \
|
||||
do { \
|
||||
if (strcmp(CRIT_FLAG, LOG_FLAG)) \
|
||||
{ \
|
||||
PRINT_LOG_FLAG(LOG_FLAG, "***CRITICAL ERROR*** " format, args); \
|
||||
} \
|
||||
PRINT_LOG_FLAG(CRIT_FLAG, "***CRITICAL ERROR*** " format, args); \
|
||||
} while(0)
|
||||
|
||||
|
||||
#define PRINT_INFO(format, args...) \
|
||||
do { \
|
||||
if (strcmp(INFO_FLAG, LOG_FLAG)) \
|
||||
@@ -361,6 +372,12 @@ do { \
|
||||
format, LOG_PREFIX, args); \
|
||||
} while(0)
|
||||
|
||||
#define PRINT_CRIT_ERROR(format, args...) \
|
||||
do { \
|
||||
PRINT(CRIT_FLAG, "%s: ***CRITICAL ERROR*** " \
|
||||
format, LOG_PREFIX, args); \
|
||||
} while(0)
|
||||
|
||||
#else
|
||||
|
||||
#define PRINT_INFO(format, args...) \
|
||||
@@ -374,6 +391,12 @@ do { \
|
||||
format, args); \
|
||||
} while(0)
|
||||
|
||||
#define PRINT_CRIT_ERROR(format, args...) \
|
||||
do { \
|
||||
PRINT(CRIT_FLAG, "***CRITICAL ERROR*** " \
|
||||
format, args); \
|
||||
} while(0)
|
||||
|
||||
#endif /* LOG_PREFIX */
|
||||
|
||||
#endif /* DEBUG */
|
||||
|
||||
@@ -377,15 +377,17 @@ int disk_exec(struct scst_cmd *cmd)
|
||||
case READ_12:
|
||||
case READ_16:
|
||||
cmd->completed = 1;
|
||||
break;
|
||||
goto out_done;
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
out_done:
|
||||
res = SCST_EXEC_COMPLETED;
|
||||
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT);
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
goto out;
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar");
|
||||
|
||||
@@ -392,15 +392,17 @@ int modisk_exec(struct scst_cmd *cmd)
|
||||
case READ_12:
|
||||
case READ_16:
|
||||
cmd->completed = 1;
|
||||
break;
|
||||
goto out_done;
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
out_done:
|
||||
res = SCST_EXEC_COMPLETED;
|
||||
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT);
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
goto out;
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar");
|
||||
|
||||
@@ -416,15 +416,17 @@ int tape_exec(struct scst_cmd *cmd)
|
||||
case WRITE_6:
|
||||
case READ_6:
|
||||
cmd->completed = 1;
|
||||
break;
|
||||
goto out_done;
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
out_done:
|
||||
res = SCST_EXEC_COMPLETED;
|
||||
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT);
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
goto out;
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar");
|
||||
|
||||
@@ -1463,7 +1463,8 @@ static int dev_user_process_scst_commands(struct scst_user_dev *dev)
|
||||
TRACE_DBG("Deleting cmd %p from active cmd list", cmd);
|
||||
list_del(&cmd->cmd_list_entry);
|
||||
spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
|
||||
scst_process_active_cmd(cmd, SCST_CONTEXT_DIRECT);
|
||||
scst_process_active_cmd(cmd, SCST_CONTEXT_DIRECT |
|
||||
SCST_CONTEXT_PROCESSABLE);
|
||||
spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
|
||||
res++;
|
||||
}
|
||||
@@ -3101,16 +3102,15 @@ static int __init init_scst_user(void)
|
||||
|
||||
dev_user_sysfs_class = class_create(THIS_MODULE, DEV_USER_NAME);
|
||||
if (IS_ERR(dev_user_sysfs_class)) {
|
||||
printk(KERN_ERR "Unable create sysfs class for SCST user "
|
||||
"space handler\n");
|
||||
PRINT_ERROR("%s", "Unable create sysfs class for SCST user "
|
||||
"space handler");
|
||||
res = PTR_ERR(dev_user_sysfs_class);
|
||||
goto out_proc;
|
||||
}
|
||||
|
||||
res = register_chrdev(DEV_USER_MAJOR, DEV_USER_NAME, &dev_user_fops);
|
||||
if (res) {
|
||||
printk(KERN_ERR "Unable to get major %d for SCSI tapes\n",
|
||||
DEV_USER_MAJOR);
|
||||
PRINT_ERROR("Unable to get major %d for SCSI tapes", DEV_USER_MAJOR);
|
||||
goto out_class;
|
||||
}
|
||||
|
||||
|
||||
@@ -256,6 +256,7 @@ static int vdisk_task_mgmt_fn(struct scst_mgmt_cmd *mcmd,
|
||||
#define VDISK_TYPE { \
|
||||
name: VDISK_NAME, \
|
||||
type: TYPE_DISK, \
|
||||
exec_sync: 1, \
|
||||
threads_num: -1, \
|
||||
parse_atomic: 1, \
|
||||
exec_atomic: 0, \
|
||||
@@ -291,6 +292,7 @@ static int vdisk_task_mgmt_fn(struct scst_mgmt_cmd *mcmd,
|
||||
#define VCDROM_TYPE { \
|
||||
name: VCDROM_NAME, \
|
||||
type: TYPE_ROM, \
|
||||
exec_sync: 1, \
|
||||
threads_num: -1, \
|
||||
parse_atomic: 1, \
|
||||
exec_atomic: 0, \
|
||||
@@ -1563,8 +1565,9 @@ static void vdisk_exec_mode_select(struct scst_cmd *cmd)
|
||||
}
|
||||
|
||||
if (!(cmd->cdb[1] & PF) || (cmd->cdb[1] & SP)) {
|
||||
PRINT_ERROR("MODE SELECT: PF and/or SP are wrongly set "
|
||||
"(cdb[1]=%x)", cmd->cdb[1]);
|
||||
TRACE(TRACE_MINOR|TRACE_SCSI, "MODE SELECT: Unsupported "
|
||||
"value(s) of PF and/or SP bits (cdb[1]=%x)",
|
||||
cmd->cdb[1]);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
|
||||
goto out_put;
|
||||
@@ -1852,17 +1855,12 @@ static void vdisk_exec_prevent_allow_medium_removal(struct scst_cmd *cmd)
|
||||
|
||||
TRACE_DBG("PERSIST/PREVENT 0x%02x", cmd->cdb[4]);
|
||||
|
||||
spin_lock(&virt_dev->flags_lock);
|
||||
if (cmd->dev->handler->type == TYPE_ROM)
|
||||
if (cmd->dev->handler->type == TYPE_ROM) {
|
||||
spin_lock(&virt_dev->flags_lock);
|
||||
virt_dev->prevent_allow_medium_removal =
|
||||
cmd->cdb[4] & 0x01 ? 1 : 0;
|
||||
else {
|
||||
PRINT_ERROR("%s", "Prevent allow medium removal for "
|
||||
"non-CDROM device");
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_opcode));
|
||||
spin_unlock(&virt_dev->flags_lock);
|
||||
}
|
||||
spin_unlock(&virt_dev->flags_lock);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ int scst_alloc_sense(struct scst_cmd *cmd, int atomic)
|
||||
|
||||
cmd->sense = mempool_alloc(scst_sense_mempool, gfp_mask);
|
||||
if (cmd->sense == NULL) {
|
||||
PRINT_ERROR("FATAL!!! Sense memory allocation failed (op %x). "
|
||||
PRINT_CRIT_ERROR("Sense memory allocation failed (op %x). "
|
||||
"The sense data will be lost!!", cmd->cdb[0]);
|
||||
res = -ENOMEM;
|
||||
goto out;
|
||||
@@ -1010,7 +1010,7 @@ static void scst_req_done(struct scsi_cmnd *scsi_cmd)
|
||||
return;
|
||||
}
|
||||
|
||||
static void scst_send_release(struct scst_tgt_dev *tgt_dev)
|
||||
static void scst_send_release(struct scst_device *dev)
|
||||
{
|
||||
struct scsi_request *req;
|
||||
struct scsi_device *scsi_dev;
|
||||
@@ -1018,10 +1018,10 @@ static void scst_send_release(struct scst_tgt_dev *tgt_dev)
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (tgt_dev->dev->scsi_dev == NULL)
|
||||
if (dev->scsi_dev == NULL)
|
||||
goto out;
|
||||
|
||||
scsi_dev = tgt_dev->dev->scsi_dev;
|
||||
scsi_dev = dev->scsi_dev;
|
||||
|
||||
req = scsi_allocate_request(scsi_dev, GFP_KERNEL);
|
||||
if (req == NULL) {
|
||||
@@ -1042,7 +1042,7 @@ static void scst_send_release(struct scst_tgt_dev *tgt_dev)
|
||||
req->sr_use_sg = 0;
|
||||
req->sr_bufflen = 0;
|
||||
req->sr_buffer = NULL;
|
||||
req->sr_request->rq_disk = tgt_dev->dev->rq_disk;
|
||||
req->sr_request->rq_disk = dev->rq_disk;
|
||||
req->sr_sense_buffer[0] = 0;
|
||||
|
||||
TRACE(TRACE_DEBUG | TRACE_SCSI, "Sending RELEASE req %p to SCSI "
|
||||
@@ -1055,7 +1055,7 @@ out:
|
||||
return;
|
||||
}
|
||||
#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) */
|
||||
static void scst_send_release(struct scst_tgt_dev *tgt_dev)
|
||||
static void scst_send_release(struct scst_device *dev)
|
||||
{
|
||||
struct scsi_device *scsi_dev;
|
||||
unsigned char cdb[6];
|
||||
@@ -1064,13 +1064,13 @@ static void scst_send_release(struct scst_tgt_dev *tgt_dev)
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (tgt_dev->dev->scsi_dev == NULL)
|
||||
if (dev->scsi_dev == NULL)
|
||||
goto out;
|
||||
|
||||
/* We can't afford missing RELEASE due to memory shortage */
|
||||
sense = kmalloc(SCST_SENSE_BUFFERSIZE, GFP_KERNEL|__GFP_NOFAIL);
|
||||
|
||||
scsi_dev = tgt_dev->dev->scsi_dev;
|
||||
scsi_dev = dev->scsi_dev;
|
||||
|
||||
for(i = 0; i < 5; i++) {
|
||||
memset(cdb, 0, sizeof(cdb));
|
||||
@@ -1092,7 +1092,7 @@ static void scst_send_release(struct scst_tgt_dev *tgt_dev)
|
||||
PRINT_ERROR("RELEASE failed: %d", rc);
|
||||
PRINT_BUFFER("RELEASE sense", sense,
|
||||
SCST_SENSE_BUFFERSIZE);
|
||||
scst_check_internal_sense(tgt_dev->dev, rc,
|
||||
scst_check_internal_sense(dev, rc,
|
||||
sense, SCST_SENSE_BUFFERSIZE);
|
||||
}
|
||||
}
|
||||
@@ -1124,11 +1124,12 @@ static void scst_clear_reservation(struct scst_tgt_dev *tgt_dev)
|
||||
&tgt_dev_tmp->tgt_dev_flags);
|
||||
}
|
||||
dev->dev_reserved = 0;
|
||||
release = 1;
|
||||
}
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
|
||||
if (release)
|
||||
scst_send_release(tgt_dev);
|
||||
scst_send_release(dev);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
@@ -1293,6 +1294,7 @@ struct scst_cmd *scst_alloc_cmd(int gfp_mask)
|
||||
cmd->state = SCST_CMD_STATE_INIT_WAIT;
|
||||
atomic_set(&cmd->cmd_ref, 1);
|
||||
cmd->cmd_lists = &scst_main_cmd_lists;
|
||||
INIT_LIST_HEAD(&cmd->mgmt_cmd_list);
|
||||
cmd->queue_type = SCST_CMD_QUEUE_SIMPLE;
|
||||
cmd->timeout = SCST_DEFAULT_TIMEOUT;
|
||||
cmd->retries = 0;
|
||||
@@ -1473,7 +1475,7 @@ struct scst_mgmt_cmd *scst_alloc_mgmt_cmd(int gfp_mask)
|
||||
|
||||
mcmd = mempool_alloc(scst_mgmt_mempool, gfp_mask);
|
||||
if (mcmd == NULL) {
|
||||
PRINT_ERROR("%s", "Allocation of management command "
|
||||
PRINT_CRIT_ERROR("%s", "Allocation of management command "
|
||||
"failed, some commands and their data could leak");
|
||||
goto out;
|
||||
}
|
||||
@@ -1555,6 +1557,7 @@ int scst_alloc_space(struct scst_cmd *cmd)
|
||||
int atomic = scst_cmd_atomic(cmd);
|
||||
int flags;
|
||||
struct scst_tgt_dev *tgt_dev = cmd->tgt_dev;
|
||||
int bufflen = cmd->bufflen;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
@@ -1563,7 +1566,21 @@ int scst_alloc_space(struct scst_cmd *cmd)
|
||||
flags = atomic ? SCST_POOL_NO_ALLOC_ON_CACHE_MISS : 0;
|
||||
if (cmd->no_sgv)
|
||||
flags |= SCST_POOL_ALLOC_NO_CACHED;
|
||||
cmd->sg = sgv_pool_alloc(tgt_dev->pool, cmd->bufflen, gfp_mask, flags,
|
||||
|
||||
if (unlikely(cmd->bufflen == 0)) {
|
||||
TRACE(TRACE_MGMT_MINOR, "Data direction %d or/and zero buffer "
|
||||
"length. Opcode 0x%x, handler %s, target %s",
|
||||
cmd->data_direction, cmd->cdb[0],
|
||||
cmd->dev->handler->name, cmd->tgtt->name);
|
||||
/*
|
||||
* Be on the safe side and alloc stub buffer. Neither target
|
||||
* drivers, nor user space will touch it, since bufflen
|
||||
* remains 0.
|
||||
*/
|
||||
bufflen = PAGE_SIZE;
|
||||
}
|
||||
|
||||
cmd->sg = sgv_pool_alloc(tgt_dev->pool, bufflen, gfp_mask, flags,
|
||||
&cmd->sg_cnt, &cmd->sgv, NULL);
|
||||
if (cmd->sg == NULL)
|
||||
goto out;
|
||||
@@ -1749,16 +1766,6 @@ int scst_get_cdb_info(const uint8_t *cdb_p, int dev_type,
|
||||
*op_flags = ptr->flags;
|
||||
*transfer_len = (*ptr->get_trans_len)(cdb_p, ptr->off);
|
||||
|
||||
#ifdef EXTRACHECKS
|
||||
if (unlikely((*transfer_len == 0) &&
|
||||
(*direction != SCST_DATA_NONE) &&
|
||||
((*op_flags & SCST_UNKNOWN_LENGTH) == 0))) {
|
||||
PRINT_ERROR("transfer_len 0, direction %d, flags %x, changing "
|
||||
"direction on NONE", *direction, *op_flags);
|
||||
*direction = SCST_DATA_NONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return res;
|
||||
@@ -2488,9 +2495,10 @@ void scst_alloc_set_UA(struct scst_tgt_dev *tgt_dev,
|
||||
|
||||
UA_entry = mempool_alloc(scst_ua_mempool, GFP_ATOMIC);
|
||||
if (UA_entry == NULL) {
|
||||
PRINT_ERROR("%s", "UNIT ATTENTION memory "
|
||||
PRINT_CRIT_ERROR("%s", "UNIT ATTENTION memory "
|
||||
"allocation failed. The UNIT ATTENTION "
|
||||
"on some sessions will be missed");
|
||||
PRINT_BUFFER("Lost UA", sense, sense_len);
|
||||
goto out;
|
||||
}
|
||||
memset(UA_entry, 0, sizeof(*UA_entry));
|
||||
@@ -2609,6 +2617,9 @@ struct scst_cmd *__scst_check_deferred_commands(struct scst_tgt_dev *tgt_dev)
|
||||
|
||||
spin_lock_irq(&tgt_dev->sn_lock);
|
||||
|
||||
if (unlikely(tgt_dev->hq_cmd_count != 0))
|
||||
goto out_unlock;
|
||||
|
||||
restart:
|
||||
list_for_each_entry_safe(cmd, t, &tgt_dev->deferred_cmd_list,
|
||||
sn_cmd_list_entry) {
|
||||
@@ -2935,16 +2946,14 @@ void scst_unblock_cmds(struct scst_device *dev)
|
||||
return;
|
||||
}
|
||||
|
||||
static struct scst_cmd *__scst_unblock_deferred(
|
||||
struct scst_tgt_dev *tgt_dev, struct scst_cmd *out_of_sn_cmd)
|
||||
static void __scst_unblock_deferred(struct scst_tgt_dev *tgt_dev,
|
||||
struct scst_cmd *out_of_sn_cmd)
|
||||
{
|
||||
struct scst_cmd *res = NULL;
|
||||
|
||||
EXTRACHECKS_BUG_ON(!out_of_sn_cmd->sn_set);
|
||||
|
||||
if (out_of_sn_cmd->sn == tgt_dev->expected_sn) {
|
||||
scst_inc_expected_sn(tgt_dev, out_of_sn_cmd->sn_slot);
|
||||
res = scst_check_deferred_commands(tgt_dev);
|
||||
scst_make_deferred_commands_active(tgt_dev, out_of_sn_cmd);
|
||||
} else {
|
||||
out_of_sn_cmd->out_of_sn = 1;
|
||||
spin_lock_irq(&tgt_dev->sn_lock);
|
||||
@@ -2957,14 +2966,12 @@ static struct scst_cmd *__scst_unblock_deferred(
|
||||
spin_unlock_irq(&tgt_dev->sn_lock);
|
||||
}
|
||||
|
||||
return res;
|
||||
return;
|
||||
}
|
||||
|
||||
void scst_unblock_deferred(struct scst_tgt_dev *tgt_dev,
|
||||
struct scst_cmd *out_of_sn_cmd)
|
||||
{
|
||||
struct scst_cmd *cmd;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (!out_of_sn_cmd->sn_set) {
|
||||
@@ -2972,16 +2979,7 @@ void scst_unblock_deferred(struct scst_tgt_dev *tgt_dev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
cmd = __scst_unblock_deferred(tgt_dev, out_of_sn_cmd);
|
||||
if (cmd != NULL) {
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&cmd->cmd_lists->cmd_list_lock, flags);
|
||||
TRACE_SN("cmd %p with sn %ld added to the head of active cmd "
|
||||
"list", cmd, cmd->sn);
|
||||
list_add(&cmd->cmd_list_entry, &cmd->cmd_lists->active_cmd_list);
|
||||
wake_up(&cmd->cmd_lists->cmd_list_waitQ);
|
||||
spin_unlock_irqrestore(&cmd->cmd_lists->cmd_list_lock, flags);
|
||||
}
|
||||
__scst_unblock_deferred(tgt_dev, out_of_sn_cmd);
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
@@ -3005,18 +3003,8 @@ void scst_on_hq_cmd_response(struct scst_cmd *cmd)
|
||||
* non-locked state. In the worst case we will only have
|
||||
* unneeded run of the deferred commands.
|
||||
*/
|
||||
if (tgt_dev->hq_cmd_count == 0) {
|
||||
struct scst_cmd *c =
|
||||
scst_check_deferred_commands(tgt_dev);
|
||||
if (c != NULL) {
|
||||
spin_lock_irq(&c->cmd_lists->cmd_list_lock);
|
||||
TRACE_SN("Adding cmd %p to active cmd list", c);
|
||||
list_add_tail(&c->cmd_list_entry,
|
||||
&c->cmd_lists->active_cmd_list);
|
||||
wake_up(&c->cmd_lists->cmd_list_waitQ);
|
||||
spin_unlock_irq(&c->cmd_lists->cmd_list_lock);
|
||||
}
|
||||
}
|
||||
if (tgt_dev->hq_cmd_count == 0)
|
||||
scst_make_deferred_commands_active(tgt_dev, cmd);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
@@ -3026,6 +3014,12 @@ void scst_xmit_process_aborted_cmd(struct scst_cmd *cmd)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_MGMT_DBG("Aborted cmd %p done (cmd_ref %d, "
|
||||
"scst_cmd_count %d)", cmd, atomic_read(&cmd->cmd_ref),
|
||||
atomic_read(&scst_cmd_count));
|
||||
|
||||
scst_done_cmd_mgmt(cmd);
|
||||
|
||||
smp_rmb();
|
||||
if (test_bit(SCST_CMD_ABORTED_OTHER, &cmd->cmd_flags)) {
|
||||
if (cmd->completed) {
|
||||
|
||||
@@ -76,6 +76,8 @@ spinlock_t scst_main_lock;
|
||||
|
||||
struct kmem_cache *scst_mgmt_cachep;
|
||||
mempool_t *scst_mgmt_mempool;
|
||||
struct kmem_cache *scst_mgmt_stub_cachep;
|
||||
mempool_t *scst_mgmt_stub_mempool;
|
||||
struct kmem_cache *scst_ua_cachep;
|
||||
mempool_t *scst_ua_mempool;
|
||||
struct kmem_cache *scst_sense_cachep;
|
||||
@@ -1558,7 +1560,10 @@ static int __init init_scst(void)
|
||||
} while (0)
|
||||
|
||||
INIT_CACHEP(scst_mgmt_cachep, scst_mgmt_cmd, out);
|
||||
INIT_CACHEP(scst_ua_cachep, scst_tgt_dev_UA, out_destroy_mgmt_cache);
|
||||
INIT_CACHEP(scst_mgmt_stub_cachep, scst_mgmt_cmd_stub,
|
||||
out_destroy_mgmt_cache);
|
||||
INIT_CACHEP(scst_ua_cachep, scst_tgt_dev_UA,
|
||||
out_destroy_mgmt_stub_cache);
|
||||
{
|
||||
struct scst_sense { uint8_t s[SCST_SENSE_BUFFERSIZE]; };
|
||||
INIT_CACHEP(scst_sense_cachep, scst_sense, out_destroy_ua_cache);
|
||||
@@ -1568,18 +1573,25 @@ static int __init init_scst(void)
|
||||
INIT_CACHEP(scst_tgtd_cachep, scst_tgt_dev, out_destroy_sess_cache);
|
||||
INIT_CACHEP(scst_acgd_cachep, scst_acg_dev, out_destroy_tgt_cache);
|
||||
|
||||
scst_mgmt_mempool = mempool_create(10, mempool_alloc_slab,
|
||||
scst_mgmt_mempool = mempool_create(64, mempool_alloc_slab,
|
||||
mempool_free_slab, scst_mgmt_cachep);
|
||||
if (scst_mgmt_mempool == NULL) {
|
||||
res = -ENOMEM;
|
||||
goto out_destroy_acg_cache;
|
||||
}
|
||||
|
||||
scst_ua_mempool = mempool_create(25, mempool_alloc_slab,
|
||||
scst_mgmt_stub_mempool = mempool_create(1024, mempool_alloc_slab,
|
||||
mempool_free_slab, scst_mgmt_stub_cachep);
|
||||
if (scst_mgmt_stub_mempool == NULL) {
|
||||
res = -ENOMEM;
|
||||
goto out_destroy_mgmt_mempool;
|
||||
}
|
||||
|
||||
scst_ua_mempool = mempool_create(64, mempool_alloc_slab,
|
||||
mempool_free_slab, scst_ua_cachep);
|
||||
if (scst_ua_mempool == NULL) {
|
||||
res = -ENOMEM;
|
||||
goto out_destroy_mgmt_mempool;
|
||||
goto out_destroy_mgmt_stub_mempool;
|
||||
}
|
||||
|
||||
/* Loosing sense may have fatal consequences, so let's have a big pool */
|
||||
@@ -1663,6 +1675,9 @@ out_destroy_sense_mempool:
|
||||
out_destroy_ua_mempool:
|
||||
mempool_destroy(scst_ua_mempool);
|
||||
|
||||
out_destroy_mgmt_stub_mempool:
|
||||
mempool_destroy(scst_mgmt_stub_mempool);
|
||||
|
||||
out_destroy_mgmt_mempool:
|
||||
mempool_destroy(scst_mgmt_mempool);
|
||||
|
||||
@@ -1684,6 +1699,9 @@ out_destroy_sense_cache:
|
||||
out_destroy_ua_cache:
|
||||
kmem_cache_destroy(scst_ua_cachep);
|
||||
|
||||
out_destroy_mgmt_stub_cache:
|
||||
kmem_cache_destroy(scst_mgmt_stub_cachep);
|
||||
|
||||
out_destroy_mgmt_cache:
|
||||
kmem_cache_destroy(scst_mgmt_cachep);
|
||||
goto out;
|
||||
@@ -1715,10 +1733,12 @@ static void __exit exit_scst(void)
|
||||
} while (0)
|
||||
|
||||
mempool_destroy(scst_mgmt_mempool);
|
||||
mempool_destroy(scst_mgmt_stub_mempool);
|
||||
mempool_destroy(scst_ua_mempool);
|
||||
mempool_destroy(scst_sense_mempool);
|
||||
|
||||
DEINIT_CACHEP(scst_mgmt_cachep);
|
||||
DEINIT_CACHEP(scst_mgmt_stub_cachep);
|
||||
DEINIT_CACHEP(scst_ua_cachep);
|
||||
DEINIT_CACHEP(scst_sense_cachep);
|
||||
DEINIT_CACHEP(scst_cmd_cachep);
|
||||
@@ -1760,7 +1780,7 @@ EXPORT_SYMBOL(scst_process_active_cmd);
|
||||
* Target Driver Side (i.e. HBA)
|
||||
*/
|
||||
EXPORT_SYMBOL(scst_register_session);
|
||||
EXPORT_SYMBOL(scst_unregister_session);
|
||||
EXPORT_SYMBOL(scst_unregister_session_ex);
|
||||
|
||||
EXPORT_SYMBOL(__scst_register_target_template);
|
||||
EXPORT_SYMBOL(scst_unregister_target_template);
|
||||
|
||||
@@ -493,7 +493,7 @@ struct scatterlist *sgv_pool_alloc(struct sgv_pool *pool, unsigned int size,
|
||||
{
|
||||
struct sgv_pool_obj *obj;
|
||||
int order, pages, cnt;
|
||||
struct scatterlist *res;
|
||||
struct scatterlist *res = NULL;
|
||||
int pages_to_alloc;
|
||||
struct kmem_cache *cache;
|
||||
int no_cached = flags & SCST_POOL_ALLOC_NO_CACHED;
|
||||
@@ -501,7 +501,8 @@ struct scatterlist *sgv_pool_alloc(struct sgv_pool *pool, unsigned int size,
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
sBUG_ON(size == 0);
|
||||
if (unlikely(size == 0))
|
||||
goto out;
|
||||
|
||||
pages = ((size + PAGE_SIZE - 1) >> PAGE_SHIFT);
|
||||
order = get_order(size);
|
||||
|
||||
@@ -96,9 +96,6 @@ extern unsigned long scst_trace_flag;
|
||||
/* Set if new commands initialization is suspended for a while */
|
||||
#define SCST_FLAG_SUSPENDED 1
|
||||
|
||||
/* Set if a TM command is being performed */
|
||||
#define SCST_FLAG_TM_ACTIVE 2
|
||||
|
||||
/**
|
||||
** Return codes for cmd state process functions
|
||||
**/
|
||||
@@ -139,6 +136,7 @@ static inline int scst_get_context(void)
|
||||
extern unsigned long scst_max_cmd_mem;
|
||||
|
||||
extern mempool_t *scst_mgmt_mempool;
|
||||
extern mempool_t *scst_mgmt_stub_mempool;
|
||||
extern mempool_t *scst_ua_mempool;
|
||||
extern mempool_t *scst_sense_mempool;
|
||||
|
||||
@@ -228,6 +226,28 @@ static inline struct scst_cmd *scst_check_deferred_commands(
|
||||
return __scst_check_deferred_commands(tgt_dev);
|
||||
}
|
||||
|
||||
static inline void scst_make_deferred_commands_active(
|
||||
struct scst_tgt_dev *tgt_dev, struct scst_cmd *curr_cmd)
|
||||
{
|
||||
struct scst_cmd *c;
|
||||
|
||||
c = __scst_check_deferred_commands(tgt_dev);
|
||||
if (c != NULL) {
|
||||
TRACE_SN("Adding cmd %p to active cmd list", c);
|
||||
|
||||
EXTRACHECKS_BUG_ON(c->cmd_lists != curr_cmd->cmd_lists);
|
||||
|
||||
spin_lock_irq(&c->cmd_lists->cmd_list_lock);
|
||||
list_add_tail(&c->cmd_list_entry,
|
||||
&c->cmd_lists->active_cmd_list);
|
||||
if (!curr_cmd->context_processable || curr_cmd->long_xmit)
|
||||
wake_up(&c->cmd_lists->cmd_list_waitQ);
|
||||
spin_unlock_irq(&c->cmd_lists->cmd_list_lock);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void scst_inc_expected_sn(struct scst_tgt_dev *tgt_dev, atomic_t *slot);
|
||||
int scst_check_hq_cmd(struct scst_cmd *cmd);
|
||||
|
||||
@@ -339,7 +359,7 @@ struct scst_cmd *__scst_find_cmd_by_tag(struct scst_session *sess,
|
||||
|
||||
struct scst_mgmt_cmd *scst_alloc_mgmt_cmd(int gfp_mask);
|
||||
void scst_free_mgmt_cmd(struct scst_mgmt_cmd *mcmd);
|
||||
void scst_complete_cmd_mgmt(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd);
|
||||
void scst_done_cmd_mgmt(struct scst_cmd *cmd);
|
||||
|
||||
/* /proc support */
|
||||
int scst_proc_init_module(void);
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
static void scst_cmd_set_sn(struct scst_cmd *cmd);
|
||||
static int __scst_init_cmd(struct scst_cmd *cmd);
|
||||
static void scst_finish_cmd_mgmt(struct scst_cmd *cmd);
|
||||
|
||||
static inline void scst_schedule_tasklet(struct scst_cmd *cmd)
|
||||
{
|
||||
@@ -49,7 +50,7 @@ static inline void scst_schedule_tasklet(struct scst_cmd *cmd)
|
||||
}
|
||||
|
||||
/*
|
||||
* Must not be called in parallel with scst_unregister_session() for the
|
||||
* Must not be called in parallel with scst_unregister_session_ex() for the
|
||||
* same sess
|
||||
*/
|
||||
struct scst_cmd *scst_rx_cmd(struct scst_session *sess,
|
||||
@@ -316,8 +317,10 @@ static int scst_pre_parse(struct scst_cmd *cmd)
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
cmd->inc_expected_sn_on_done = !dev->has_own_order_mgmt &&
|
||||
(dev->queue_alg == SCST_CONTR_MODE_QUEUE_ALG_RESTRICTED_REORDER);
|
||||
cmd->inc_expected_sn_on_done = dev->handler->exec_sync ||
|
||||
(!dev->has_own_order_mgmt &&
|
||||
((dev->queue_alg == SCST_CONTR_MODE_QUEUE_ALG_RESTRICTED_REORDER) ||
|
||||
(cmd->queue_type == SCST_CMD_QUEUE_ORDERED)));
|
||||
|
||||
sBUG_ON(cmd->internal);
|
||||
|
||||
@@ -425,7 +428,6 @@ static int scst_parse_cmd(struct scst_cmd *cmd)
|
||||
struct scst_device *dev = cmd->dev;
|
||||
int atomic = scst_cmd_atomic(cmd);
|
||||
int orig_bufflen = cmd->bufflen;
|
||||
scst_data_direction orig_data_direction = cmd->data_direction;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
@@ -514,36 +516,35 @@ static int scst_parse_cmd(struct scst_cmd *cmd)
|
||||
cmd->data_direction = cmd->expected_data_direction;
|
||||
cmd->bufflen = cmd->expected_transfer_len;
|
||||
#else
|
||||
if (unlikely(cmd->data_direction != orig_data_direction)) {
|
||||
if (unlikely(cmd->data_direction != cmd->expected_data_direction)) {
|
||||
PRINT_ERROR("Expected data direction %d for opcode "
|
||||
"0x%02x (handler %s, target %s) doesn't match "
|
||||
"decoded value %d", cmd->data_direction,
|
||||
cmd->cdb[0], dev->handler->name,
|
||||
cmd->tgtt->name, orig_data_direction);
|
||||
cmd->tgtt->name, cmd->expected_data_direction);
|
||||
scst_set_cmd_error(cmd,
|
||||
SCST_LOAD_SENSE(scst_sense_invalid_message));
|
||||
goto out_dev_done;
|
||||
}
|
||||
if (unlikely(cmd->bufflen != cmd->expected_transfer_len)) {
|
||||
PRINT_INFO("Warning: expected transfer length %d for "
|
||||
"opcode 0x%02x (handler %s, target %s) doesn't "
|
||||
"match decoded value %d. Faulty initiator "
|
||||
"(e.g. VMware is known to be such) or "
|
||||
TRACE(TRACE_MINOR, "Warning: expected transfer length "
|
||||
"%d for opcode 0x%02x (handler %s, target %s) "
|
||||
"doesn't match decoded value %d. Faulty "
|
||||
"initiator (e.g. VMware is known to be such) or "
|
||||
"scst_scsi_op_table should be updated?",
|
||||
cmd->expected_transfer_len, cmd->cdb[0],
|
||||
dev->handler->name, cmd->tgtt->name,
|
||||
cmd->bufflen);
|
||||
PRINT_BUFFER("Suspicious CDB", cmd->cdb, cmd->cdb_len);
|
||||
PRINT_BUFF_FLAG(TRACE_MINOR, "Suspicious CDB", cmd->cdb,
|
||||
cmd->cdb_len);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((cmd->data_direction == SCST_DATA_UNKNOWN) ||
|
||||
((cmd->bufflen == 0) && (cmd->data_direction != SCST_DATA_NONE))) {
|
||||
PRINT_ERROR("Wrong data direction (%d) or/and buffer "
|
||||
"length (%d). Opcode 0x%x, handler %s, target %s",
|
||||
cmd->data_direction, cmd->bufflen, cmd->cdb[0],
|
||||
dev->handler->name, cmd->tgtt->name);
|
||||
if (unlikely(cmd->data_direction == SCST_DATA_UNKNOWN)) {
|
||||
PRINT_ERROR("Unknown data direction. Opcode 0x%x, handler %s, "
|
||||
"target %s", cmd->cdb[0], dev->handler->name,
|
||||
cmd->tgtt->name);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
@@ -618,6 +619,11 @@ static int scst_prepare_space(struct scst_cmd *cmd)
|
||||
if (r > 0)
|
||||
goto alloc;
|
||||
else if (r == 0) {
|
||||
if (unlikely(cmd->bufflen == 0)) {
|
||||
/* See comment in scst_alloc_space() */
|
||||
if (cmd->sg == NULL)
|
||||
goto alloc;
|
||||
}
|
||||
cmd->data_buf_alloced = 1;
|
||||
if (unlikely(orig_bufflen < cmd->bufflen)) {
|
||||
PRINT_ERROR("Target driver allocated data "
|
||||
@@ -626,16 +632,13 @@ static int scst_prepare_space(struct scst_cmd *cmd)
|
||||
cmd->bufflen);
|
||||
goto out_error;
|
||||
}
|
||||
} else
|
||||
goto check;
|
||||
TRACE_MEM("%s", "data_buf_alloced, returning");
|
||||
}
|
||||
goto check;
|
||||
}
|
||||
|
||||
alloc:
|
||||
if (!cmd->data_buf_alloced) {
|
||||
r = scst_alloc_space(cmd);
|
||||
} else {
|
||||
TRACE_MEM("%s", "data_buf_alloced set, returning");
|
||||
}
|
||||
r = scst_alloc_space(cmd);
|
||||
|
||||
check:
|
||||
if (r != 0) {
|
||||
@@ -912,11 +915,13 @@ void scst_proccess_redirect_cmd(struct scst_cmd *cmd, int context,
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_DBG("Context: %d", context);
|
||||
TRACE_DBG("Context: %x", context);
|
||||
|
||||
switch(context) {
|
||||
case SCST_CONTEXT_DIRECT:
|
||||
switch(context & ~SCST_CONTEXT_PROCESSABLE) {
|
||||
case SCST_CONTEXT_DIRECT_ATOMIC:
|
||||
context &= ~SCST_CONTEXT_PROCESSABLE;
|
||||
/* go through */
|
||||
case SCST_CONTEXT_DIRECT:
|
||||
if (check_retries)
|
||||
scst_check_retries(cmd->tgt);
|
||||
scst_process_active_cmd(cmd, context);
|
||||
@@ -1188,6 +1193,8 @@ out:
|
||||
|
||||
static void scst_cmd_done_local(struct scst_cmd *cmd, int next_state)
|
||||
{
|
||||
int context;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
#ifdef MEASURE_LATENCY
|
||||
@@ -1235,8 +1242,10 @@ static void scst_cmd_done_local(struct scst_cmd *cmd, int next_state)
|
||||
#endif
|
||||
cmd->state = next_state;
|
||||
|
||||
scst_proccess_redirect_cmd(cmd,
|
||||
scst_optimize_post_exec_context(cmd, scst_get_context()), 0);
|
||||
context = scst_optimize_post_exec_context(cmd, scst_get_context());
|
||||
if (cmd->context_processable)
|
||||
context |= SCST_CONTEXT_PROCESSABLE;
|
||||
scst_proccess_redirect_cmd(cmd, context, 0);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
@@ -1668,14 +1677,16 @@ static inline int scst_local_exec(struct scst_cmd *cmd)
|
||||
static int scst_do_send_to_midlev(struct scst_cmd *cmd)
|
||||
{
|
||||
int rc = SCST_EXEC_NOT_COMPLETED;
|
||||
struct scst_device *dev = cmd->dev;
|
||||
struct scst_dev_type *handler = dev->handler;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
/* Check here to let an out of SN cmd be queued w/o context switch */
|
||||
if (scst_cmd_atomic(cmd) && !cmd->dev->handler->exec_atomic) {
|
||||
if (scst_cmd_atomic(cmd) && !handler->exec_atomic) {
|
||||
TRACE_DBG("Dev handler %s exec() can not be "
|
||||
"called in atomic context, rescheduling to the thread",
|
||||
cmd->dev->handler->name);
|
||||
handler->name);
|
||||
rc = SCST_EXEC_NEED_THREAD;
|
||||
goto out;
|
||||
}
|
||||
@@ -1704,15 +1715,17 @@ static int scst_do_send_to_midlev(struct scst_cmd *cmd)
|
||||
goto out_rc_error;
|
||||
}
|
||||
|
||||
if (cmd->dev->handler->exec) {
|
||||
struct scst_device *dev = cmd->dev;
|
||||
if (!handler->exec_sync)
|
||||
cmd->context_processable = 0;
|
||||
|
||||
if (handler->exec) {
|
||||
TRACE_DBG("Calling dev handler %s exec(%p)",
|
||||
dev->handler->name, cmd);
|
||||
handler->name, cmd);
|
||||
TRACE_BUFF_FLAG(TRACE_SND_TOP, "Execing: ", cmd->cdb, cmd->cdb_len);
|
||||
cmd->scst_cmd_done = scst_cmd_done_local;
|
||||
rc = dev->handler->exec(cmd);
|
||||
rc = handler->exec(cmd);
|
||||
TRACE_DBG("Dev handler %s exec() returned %d",
|
||||
dev->handler->name, rc);
|
||||
handler->name, rc);
|
||||
if (rc == SCST_EXEC_COMPLETED)
|
||||
goto out;
|
||||
else if (rc == SCST_EXEC_NEED_THREAD)
|
||||
@@ -1723,7 +1736,7 @@ static int scst_do_send_to_midlev(struct scst_cmd *cmd)
|
||||
|
||||
TRACE_DBG("Sending cmd %p to SCSI mid-level", cmd);
|
||||
|
||||
if (unlikely(cmd->dev->scsi_dev == NULL)) {
|
||||
if (unlikely(dev->scsi_dev == NULL)) {
|
||||
PRINT_ERROR("Command for virtual device must be "
|
||||
"processed by device handler (lun %Ld)!",
|
||||
(uint64_t)cmd->lun);
|
||||
@@ -1751,7 +1764,7 @@ static int scst_do_send_to_midlev(struct scst_cmd *cmd)
|
||||
cmd->scsi_req->sr_bufflen, scst_cmd_done, cmd->timeout,
|
||||
cmd->retries);
|
||||
#else
|
||||
rc = scst_exec_req(cmd->dev->scsi_dev, cmd->cdb, cmd->cdb_len,
|
||||
rc = scst_exec_req(dev->scsi_dev, cmd->cdb, cmd->cdb_len,
|
||||
cmd->data_direction, cmd->sg, cmd->bufflen, cmd->sg_cnt,
|
||||
cmd->timeout, cmd->retries, cmd, scst_cmd_done,
|
||||
scst_cmd_atomic(cmd) ? GFP_ATOMIC : GFP_KERNEL);
|
||||
@@ -1780,7 +1793,7 @@ out_clear:
|
||||
|
||||
out_rc_error:
|
||||
PRINT_ERROR("Dev handler %s exec() or scst_local_exec() returned "
|
||||
"invalid code %d", cmd->dev->handler->name, rc);
|
||||
"invalid code %d", handler->name, rc);
|
||||
/* go through */
|
||||
|
||||
out_error:
|
||||
@@ -1953,6 +1966,7 @@ exec:
|
||||
count = 0;
|
||||
while(1) {
|
||||
atomic_t *slot = cmd->sn_slot;
|
||||
/* For HQ commands SN is not set */
|
||||
int inc_expected_sn = !cmd->inc_expected_sn_on_done &&
|
||||
cmd->sn_set;
|
||||
|
||||
@@ -2359,21 +2373,10 @@ out:
|
||||
|
||||
static void scst_inc_check_expected_sn(struct scst_cmd *cmd)
|
||||
{
|
||||
struct scst_cmd *c;
|
||||
|
||||
if (likely(cmd->sn_set))
|
||||
scst_inc_expected_sn(cmd->tgt_dev, cmd->sn_slot);
|
||||
|
||||
c = scst_check_deferred_commands(cmd->tgt_dev);
|
||||
if (c != NULL) {
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&c->cmd_lists->cmd_list_lock, flags);
|
||||
TRACE_SN("Adding cmd %p to active cmd list", c);
|
||||
list_add_tail(&c->cmd_list_entry,
|
||||
&c->cmd_lists->active_cmd_list);
|
||||
wake_up(&c->cmd_lists->cmd_list_waitQ);
|
||||
spin_unlock_irqrestore(&c->cmd_lists->cmd_list_lock, flags);
|
||||
}
|
||||
scst_make_deferred_commands_active(cmd->tgt_dev, cmd);
|
||||
}
|
||||
|
||||
static int scst_dev_done(struct scst_cmd *cmd)
|
||||
@@ -2503,6 +2506,9 @@ static int scst_pre_xmit_response(struct scst_cmd *cmd)
|
||||
list_del(&cmd->search_cmd_list_entry);
|
||||
spin_unlock_irq(&cmd->sess->sess_list_lock);
|
||||
|
||||
cmd->done = 1;
|
||||
smp_mb(); /* to sync with scst_abort_cmd() */
|
||||
|
||||
if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)))
|
||||
scst_xmit_process_aborted_cmd(cmd);
|
||||
|
||||
@@ -2662,16 +2668,11 @@ static int scst_finish_cmd(struct scst_cmd *cmd)
|
||||
smp_mb(); /* to sync with scst_abort_cmd() */
|
||||
|
||||
if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))) {
|
||||
unsigned long flags;
|
||||
|
||||
TRACE_MGMT_DBG("Aborted cmd %p finished (cmd_ref %d, "
|
||||
"scst_cmd_count %d)", cmd, atomic_read(&cmd->cmd_ref),
|
||||
atomic_read(&scst_cmd_count));
|
||||
|
||||
spin_lock_irqsave(&scst_mcmd_lock, flags);
|
||||
if (cmd->mgmt_cmnd)
|
||||
scst_complete_cmd_mgmt(cmd, cmd->mgmt_cmnd);
|
||||
spin_unlock_irqrestore(&scst_mcmd_lock, flags);
|
||||
scst_finish_cmd_mgmt(cmd);
|
||||
}
|
||||
|
||||
if (unlikely(cmd->delivery_status != SCST_CMD_DELIVERY_SUCCESS)) {
|
||||
@@ -2808,6 +2809,7 @@ ordered:
|
||||
tgt_dev->cur_sn_slot-tgt_dev->sn_slots);
|
||||
|
||||
cmd->sn_set = 1;
|
||||
|
||||
out:
|
||||
return;
|
||||
}
|
||||
@@ -3060,8 +3062,13 @@ void scst_process_active_cmd(struct scst_cmd *cmd, int context)
|
||||
|
||||
EXTRACHECKS_BUG_ON(in_irq());
|
||||
|
||||
cmd->context_processable = context | SCST_CONTEXT_PROCESSABLE;
|
||||
context &= ~SCST_CONTEXT_PROCESSABLE;
|
||||
cmd->atomic = (context == SCST_CONTEXT_DIRECT_ATOMIC);
|
||||
|
||||
TRACE_DBG("cmd %p, context_processable %d, atomic %d", cmd,
|
||||
cmd->context_processable, cmd->atomic);
|
||||
|
||||
do {
|
||||
switch (cmd->state) {
|
||||
case SCST_CMD_STATE_PRE_PARSE:
|
||||
@@ -3189,8 +3196,11 @@ static void scst_do_job_active(struct list_head *cmd_list,
|
||||
TRACE_ENTRY();
|
||||
|
||||
#ifdef EXTRACHECKS
|
||||
WARN_ON((context != SCST_CONTEXT_DIRECT_ATOMIC) &&
|
||||
(context != SCST_CONTEXT_DIRECT));
|
||||
{
|
||||
int c = context & ~SCST_CONTEXT_PROCESSABLE;
|
||||
sBUG_ON((c != SCST_CONTEXT_DIRECT_ATOMIC) &&
|
||||
(c != SCST_CONTEXT_DIRECT));
|
||||
}
|
||||
#endif
|
||||
|
||||
while (!list_empty(cmd_list)) {
|
||||
@@ -3255,7 +3265,8 @@ int scst_cmd_thread(void *arg)
|
||||
}
|
||||
|
||||
scst_do_job_active(&p_cmd_lists->active_cmd_list,
|
||||
&p_cmd_lists->cmd_list_lock, SCST_CONTEXT_DIRECT);
|
||||
&p_cmd_lists->cmd_list_lock, SCST_CONTEXT_DIRECT |
|
||||
SCST_CONTEXT_PROCESSABLE);
|
||||
}
|
||||
spin_unlock_irq(&p_cmd_lists->cmd_list_lock);
|
||||
|
||||
@@ -3337,45 +3348,104 @@ out:
|
||||
return res;
|
||||
}
|
||||
|
||||
/* scst_mcmd_lock supposed to be held and IRQ off */
|
||||
void scst_complete_cmd_mgmt(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd)
|
||||
/* No locks */
|
||||
void scst_done_cmd_mgmt(struct scst_cmd *cmd)
|
||||
{
|
||||
struct scst_mgmt_cmd_stub *mstb;
|
||||
bool wake = 0;
|
||||
unsigned long flags;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_MGMT_DBG("cmd %p completed (tag %llu, mcmd %p, "
|
||||
"mcmd->cmd_wait_count %d)", cmd, cmd->tag, mcmd,
|
||||
mcmd->cmd_wait_count);
|
||||
TRACE_MGMT_DBG("cmd %p done (tag %llu)", cmd, cmd->tag);
|
||||
|
||||
cmd->mgmt_cmnd = NULL;
|
||||
spin_lock_irqsave(&scst_mcmd_lock, flags);
|
||||
|
||||
if (cmd->completed)
|
||||
mcmd->completed_cmd_count++;
|
||||
list_for_each_entry(mstb, &cmd->mgmt_cmd_list,
|
||||
cmd_mgmt_cmd_list_entry) {
|
||||
struct scst_mgmt_cmd *mcmd = mstb->mcmd;
|
||||
|
||||
mcmd->cmd_wait_count--;
|
||||
if (mcmd->cmd_wait_count > 0) {
|
||||
TRACE_MGMT_DBG("cmd_wait_count(%d) not 0, skipping",
|
||||
mcmd->cmd_wait_count);
|
||||
goto out_unlock;
|
||||
TRACE_MGMT_DBG("mcmd %p, mcmd->cmd_done_wait_count %d",
|
||||
mcmd, mcmd->cmd_done_wait_count);
|
||||
|
||||
mcmd->cmd_done_wait_count--;
|
||||
if (mcmd->cmd_done_wait_count > 0) {
|
||||
TRACE_MGMT_DBG("cmd_done_wait_count(%d) not 0, "
|
||||
"skipping", mcmd->cmd_done_wait_count);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mcmd->completed) {
|
||||
sBUG_ON(mcmd->nexus_loss_check_done);
|
||||
mcmd->nexus_loss_check_active = 1;
|
||||
mcmd->state = SCST_MGMT_CMD_STATE_CHECK_NEXUS_LOSS;
|
||||
TRACE_MGMT_DBG("Adding mgmt cmd %p to active mgmt cmd "
|
||||
"list", mcmd);
|
||||
list_add_tail(&mcmd->mgmt_cmd_list_entry,
|
||||
&scst_active_mgmt_cmd_list);
|
||||
wake = 1;
|
||||
}
|
||||
}
|
||||
|
||||
mcmd->state = SCST_MGMT_CMD_STATE_DONE;
|
||||
spin_unlock_irqrestore(&scst_mcmd_lock, flags);
|
||||
|
||||
if (mcmd->completed) {
|
||||
TRACE_MGMT_DBG("Adding mgmt cmd %p to active mgmt cmd list",
|
||||
mcmd);
|
||||
list_add_tail(&mcmd->mgmt_cmd_list_entry,
|
||||
&scst_active_mgmt_cmd_list);
|
||||
}
|
||||
if (wake)
|
||||
wake_up(&scst_mgmt_cmd_list_waitQ);
|
||||
|
||||
wake_up(&scst_mgmt_cmd_list_waitQ);
|
||||
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_irq(&scst_mcmd_lock);
|
||||
goto out;
|
||||
/* No locks */
|
||||
static void scst_finish_cmd_mgmt(struct scst_cmd *cmd)
|
||||
{
|
||||
struct scst_mgmt_cmd_stub *mstb, *t;
|
||||
bool wake = 0;
|
||||
unsigned long flags;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_MGMT_DBG("cmd %p finished (tag %llu)", cmd, cmd->tag);
|
||||
|
||||
spin_lock_irqsave(&scst_mcmd_lock, flags);
|
||||
|
||||
list_for_each_entry_safe(mstb, t, &cmd->mgmt_cmd_list,
|
||||
cmd_mgmt_cmd_list_entry) {
|
||||
struct scst_mgmt_cmd *mcmd = mstb->mcmd;
|
||||
|
||||
TRACE_MGMT_DBG("mcmd %p, mcmd->cmd_finish_wait_count %d",
|
||||
mcmd, mcmd->cmd_finish_wait_count);
|
||||
|
||||
list_del(&mstb->cmd_mgmt_cmd_list_entry);
|
||||
mempool_free(mstb, scst_mgmt_stub_mempool);
|
||||
|
||||
if (cmd->completed)
|
||||
mcmd->completed_cmd_count++;
|
||||
|
||||
mcmd->cmd_finish_wait_count--;
|
||||
if (mcmd->cmd_finish_wait_count > 0) {
|
||||
TRACE_MGMT_DBG("cmd_finish_wait_count(%d) not 0, "
|
||||
"skipping", mcmd->cmd_finish_wait_count);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mcmd->completed && !mcmd->nexus_loss_check_active) {
|
||||
mcmd->state = SCST_MGMT_CMD_STATE_DONE;
|
||||
TRACE_MGMT_DBG("Adding mgmt cmd %p to active mgmt cmd "
|
||||
"list", mcmd);
|
||||
list_add_tail(&mcmd->mgmt_cmd_list_entry,
|
||||
&scst_active_mgmt_cmd_list);
|
||||
wake = 1;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&scst_mcmd_lock, flags);
|
||||
|
||||
if (wake)
|
||||
wake_up(&scst_mgmt_cmd_list_waitQ);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
static int scst_call_dev_task_mgmt_fn(struct scst_mgmt_cmd *mcmd,
|
||||
@@ -3435,7 +3505,10 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
|
||||
clear_bit(SCST_CMD_ABORTED_OTHER, &cmd->cmd_flags);
|
||||
}
|
||||
set_bit(SCST_CMD_ABORTED, &cmd->cmd_flags);
|
||||
/* To sync with cmd->finished set in scst_finish_cmd() */
|
||||
/*
|
||||
* To sync with cmd->finished/done set in
|
||||
* scst_finish_cmd()/scst_pre_xmit_response()
|
||||
*/
|
||||
smp_mb__after_set_bit();
|
||||
|
||||
if (cmd->tgt_dev == NULL) {
|
||||
@@ -3453,6 +3526,23 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
|
||||
|
||||
spin_lock_irqsave(&scst_mcmd_lock, flags);
|
||||
if ((mcmd != NULL) && !cmd->finished) {
|
||||
struct scst_mgmt_cmd_stub *mstb;
|
||||
|
||||
mstb = mempool_alloc(scst_mgmt_stub_mempool, GFP_ATOMIC);
|
||||
if (mstb == NULL) {
|
||||
PRINT_CRIT_ERROR("Allocation of management command "
|
||||
"stub failed (mcmd %p, cmd %p)", mcmd, cmd);
|
||||
goto unlock;
|
||||
}
|
||||
mstb->mcmd = mcmd;
|
||||
|
||||
/*
|
||||
* cmd can't die here or sess_list_lock already taken and
|
||||
* cmd is in the search list
|
||||
*/
|
||||
list_add_tail(&mstb->cmd_mgmt_cmd_list_entry,
|
||||
&cmd->mgmt_cmd_list);
|
||||
|
||||
/*
|
||||
* Delay the response until the command's finish in
|
||||
* order to guarantee that "no further responses from
|
||||
@@ -3461,27 +3551,19 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
|
||||
* we must wait here to be sure that we won't receive
|
||||
* double commands with the same tag.
|
||||
*/
|
||||
TRACE_MGMT_DBG("cmd %p (tag %llu) being executed/xmitted "
|
||||
"(state %d), deferring ABORT...", cmd, cmd->tag,
|
||||
cmd->state);
|
||||
#ifdef EXTRACHECKS
|
||||
if (cmd->mgmt_cmnd) {
|
||||
printk(KERN_ALERT "cmd %p (tag %llu, state %d) "
|
||||
"has non-NULL mgmt_cmnd %p!!! Current "
|
||||
"mcmd %p\n", cmd, cmd->tag, cmd->state,
|
||||
cmd->mgmt_cmnd, mcmd);
|
||||
TRACE_MGMT_DBG("cmd %p (tag %llu) being executed/"
|
||||
"xmitted (state %d), deferring ABORT...",
|
||||
cmd, cmd->tag, cmd->state);
|
||||
|
||||
mcmd->cmd_finish_wait_count++;
|
||||
|
||||
if (!cmd->done) {
|
||||
TRACE_MGMT_DBG("cmd %p (tag %llu) not done yet",
|
||||
cmd, cmd->tag);
|
||||
mcmd->cmd_done_wait_count++;
|
||||
}
|
||||
#endif
|
||||
sBUG_ON(cmd->mgmt_cmnd);
|
||||
|
||||
mcmd->cmd_wait_count++;
|
||||
|
||||
/*
|
||||
* cmd can't die here or sess_list_lock already taken and cmd is
|
||||
* in the search list
|
||||
*/
|
||||
cmd->mgmt_cmnd = mcmd;
|
||||
}
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&scst_mcmd_lock, flags);
|
||||
|
||||
tm_dbg_release_cmd(cmd);
|
||||
@@ -3494,17 +3576,31 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
|
||||
static int scst_set_mcmd_next_state(struct scst_mgmt_cmd *mcmd)
|
||||
{
|
||||
int res;
|
||||
|
||||
spin_lock_irq(&scst_mcmd_lock);
|
||||
if (mcmd->cmd_wait_count != 0) {
|
||||
TRACE_MGMT_DBG("cmd_wait_count(%d) not 0, preparing to "
|
||||
"wait", mcmd->cmd_wait_count);
|
||||
|
||||
if (mcmd->cmd_finish_wait_count == 0) {
|
||||
if (!mcmd->nexus_loss_check_done)
|
||||
mcmd->state = SCST_MGMT_CMD_STATE_CHECK_NEXUS_LOSS;
|
||||
else
|
||||
mcmd->state = SCST_MGMT_CMD_STATE_DONE;
|
||||
res = 0;
|
||||
} else if ((mcmd->cmd_done_wait_count == 0) &&
|
||||
(!mcmd->nexus_loss_check_done)) {
|
||||
mcmd->state = SCST_MGMT_CMD_STATE_CHECK_NEXUS_LOSS;
|
||||
res = 0;
|
||||
goto out_unlock;
|
||||
} else {
|
||||
TRACE_MGMT_DBG("cmd_finish_wait_count(%d) not 0, preparing to "
|
||||
"wait", mcmd->cmd_finish_wait_count);
|
||||
mcmd->state = SCST_MGMT_CMD_STATE_EXECUTING;
|
||||
res = -1;
|
||||
} else {
|
||||
mcmd->state = SCST_MGMT_CMD_STATE_DONE;
|
||||
res = 0;
|
||||
}
|
||||
|
||||
mcmd->nexus_loss_check_active = 0;
|
||||
mcmd->completed = 1;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_irq(&scst_mcmd_lock);
|
||||
return res;
|
||||
}
|
||||
@@ -3578,7 +3674,7 @@ static void scst_unblock_aborted_cmds(int scst_mutex_held)
|
||||
}
|
||||
|
||||
static void __scst_abort_task_set(struct scst_mgmt_cmd *mcmd,
|
||||
struct scst_tgt_dev *tgt_dev, int other_ini, int scst_mutex_held)
|
||||
struct scst_tgt_dev *tgt_dev, int other_ini)
|
||||
{
|
||||
struct scst_cmd *cmd;
|
||||
struct scst_session *sess = tgt_dev->sess;
|
||||
@@ -3604,8 +3700,6 @@ static void __scst_abort_task_set(struct scst_mgmt_cmd *mcmd,
|
||||
}
|
||||
spin_unlock_irq(&sess->sess_list_lock);
|
||||
|
||||
scst_unblock_aborted_cmds(scst_mutex_held);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
@@ -3626,7 +3720,10 @@ static int scst_abort_task_set(struct scst_mgmt_cmd *mcmd)
|
||||
__scst_block_dev(dev);
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
|
||||
__scst_abort_task_set(mcmd, tgt_dev, 0, 0);
|
||||
__scst_abort_task_set(mcmd, tgt_dev, 0);
|
||||
|
||||
scst_unblock_aborted_cmds(0);
|
||||
|
||||
scst_call_dev_task_mgmt_fn(mcmd, tgt_dev, 0);
|
||||
|
||||
res = scst_set_mcmd_next_state(mcmd);
|
||||
@@ -3682,7 +3779,7 @@ static int scst_clear_task_set(struct scst_mgmt_cmd *mcmd)
|
||||
__scst_block_dev(dev);
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
|
||||
__scst_abort_task_set(mcmd, mcmd->mcmd_tgt_dev, 0, 0);
|
||||
__scst_abort_task_set(mcmd, mcmd->mcmd_tgt_dev, 0);
|
||||
|
||||
mutex_lock(&scst_mutex);
|
||||
|
||||
@@ -3714,9 +3811,9 @@ static int scst_clear_task_set(struct scst_mgmt_cmd *mcmd)
|
||||
&UA_tgt_devs);
|
||||
}
|
||||
|
||||
mutex_unlock(&scst_mutex);
|
||||
scst_unblock_aborted_cmds(1);
|
||||
|
||||
scst_unblock_aborted_cmds(0);
|
||||
mutex_unlock(&scst_mutex);
|
||||
|
||||
if (!dev->tas) {
|
||||
list_for_each_entry(tgt_dev, &UA_tgt_devs, extra_tgt_dev_list_entry) {
|
||||
@@ -3736,23 +3833,6 @@ static int scst_clear_task_set(struct scst_mgmt_cmd *mcmd)
|
||||
return res;
|
||||
}
|
||||
|
||||
static int scst_check_delay_mgmt_cmd(struct scst_mgmt_cmd *mcmd)
|
||||
{
|
||||
if (test_bit(SCST_FLAG_TM_ACTIVE, &scst_flags) && !mcmd->active) {
|
||||
TRACE_MGMT_DBG("Adding mgmt cmd %p to delayed mgmt cmd list",
|
||||
mcmd);
|
||||
spin_lock_irq(&scst_mcmd_lock);
|
||||
list_add_tail(&mcmd->mgmt_cmd_list_entry,
|
||||
&scst_delayed_mgmt_cmd_list);
|
||||
spin_unlock_irq(&scst_mcmd_lock);
|
||||
return -1;
|
||||
} else {
|
||||
mcmd->active = 1;
|
||||
set_bit(SCST_FLAG_TM_ACTIVE, &scst_flags);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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)
|
||||
@@ -3761,10 +3841,6 @@ static int scst_mgmt_cmd_init(struct scst_mgmt_cmd *mcmd)
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
res = scst_check_delay_mgmt_cmd(mcmd);
|
||||
if (res != 0)
|
||||
goto out;
|
||||
|
||||
mcmd->state = SCST_MGMT_CMD_STATE_READY;
|
||||
|
||||
switch (mcmd->fn) {
|
||||
@@ -3895,6 +3971,8 @@ static int scst_target_reset(struct scst_mgmt_cmd *mcmd)
|
||||
tm_dbg_task_mgmt(dev, "TARGET RESET", 0);
|
||||
}
|
||||
|
||||
scst_unblock_aborted_cmds(1);
|
||||
|
||||
/*
|
||||
* We suppose here that for all commands that already on devices
|
||||
* on/after scsi_reset_provider() completion callbacks will be called.
|
||||
@@ -3964,6 +4042,8 @@ static int scst_lun_reset(struct scst_mgmt_cmd *mcmd)
|
||||
dev->scsi_dev->was_reset = 0;
|
||||
}
|
||||
|
||||
scst_unblock_aborted_cmds(0);
|
||||
|
||||
out_tm_dbg:
|
||||
tm_dbg_task_mgmt(mcmd->mcmd_tgt_dev->dev, "LUN RESET", 0);
|
||||
|
||||
@@ -3973,6 +4053,28 @@ out_tm_dbg:
|
||||
return res;
|
||||
}
|
||||
|
||||
/* scst_mutex supposed to be held */
|
||||
static void scst_do_nexus_loss_sess(struct scst_mgmt_cmd *mcmd)
|
||||
{
|
||||
int i;
|
||||
struct scst_session *sess = mcmd->sess;
|
||||
struct scst_tgt_dev *tgt_dev;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
for(i = 0; i < TGT_DEV_HASH_SIZE; i++) {
|
||||
struct list_head *sess_tgt_dev_list_head =
|
||||
&sess->sess_tgt_dev_list_hash[i];
|
||||
list_for_each_entry(tgt_dev, sess_tgt_dev_list_head,
|
||||
sess_tgt_dev_list_entry) {
|
||||
scst_nexus_loss(tgt_dev);
|
||||
}
|
||||
}
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Returns 0 if the command processing should be continued, <0 otherwise */
|
||||
static int scst_abort_all_nexus_loss_sess(struct scst_mgmt_cmd *mcmd,
|
||||
int nexus_loss)
|
||||
@@ -3992,9 +4094,11 @@ static int scst_abort_all_nexus_loss_sess(struct scst_mgmt_cmd *mcmd,
|
||||
mcmd);
|
||||
}
|
||||
|
||||
mcmd->needs_unblocking = 1;
|
||||
if (mcmd->fn != SCST_UNREG_SESS_TM)
|
||||
mcmd->needs_unblocking = 1;
|
||||
|
||||
mutex_lock(&scst_mutex);
|
||||
|
||||
for(i = 0; i < TGT_DEV_HASH_SIZE; i++) {
|
||||
struct list_head *sess_tgt_dev_list_head =
|
||||
&sess->sess_tgt_dev_list_hash[i];
|
||||
@@ -4002,20 +4106,23 @@ static int scst_abort_all_nexus_loss_sess(struct scst_mgmt_cmd *mcmd,
|
||||
sess_tgt_dev_list_entry) {
|
||||
struct scst_device *dev = tgt_dev->dev;
|
||||
int rc;
|
||||
|
||||
if (mcmd->fn != SCST_UNREG_SESS_TM) {
|
||||
spin_lock_bh(&dev->dev_lock);
|
||||
__scst_block_dev(dev);
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
}
|
||||
|
||||
spin_lock_bh(&dev->dev_lock);
|
||||
__scst_block_dev(dev);
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
|
||||
__scst_abort_task_set(mcmd, tgt_dev, 0, 1);
|
||||
if (nexus_loss)
|
||||
scst_nexus_loss(tgt_dev);
|
||||
__scst_abort_task_set(mcmd, tgt_dev, 0);
|
||||
|
||||
rc = scst_call_dev_task_mgmt_fn(mcmd, tgt_dev, 0);
|
||||
if ((rc < 0) && (mcmd->status == SCST_MGMT_STATUS_SUCCESS))
|
||||
mcmd->status = rc;
|
||||
}
|
||||
}
|
||||
|
||||
scst_unblock_aborted_cmds(1);
|
||||
|
||||
mutex_unlock(&scst_mutex);
|
||||
|
||||
res = scst_set_mcmd_next_state(mcmd);
|
||||
@@ -4024,7 +4131,31 @@ static int scst_abort_all_nexus_loss_sess(struct scst_mgmt_cmd *mcmd,
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Returns 0 if the command processing should be continued, <0 otherwise */
|
||||
/* scst_mutex supposed to be held */
|
||||
static void scst_do_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd)
|
||||
{
|
||||
int i;
|
||||
struct scst_tgt *tgt = mcmd->sess->tgt;
|
||||
struct scst_session *sess;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
|
||||
for(i = 0; i < TGT_DEV_HASH_SIZE; i++) {
|
||||
struct list_head *sess_tgt_dev_list_head =
|
||||
&sess->sess_tgt_dev_list_hash[i];
|
||||
struct scst_tgt_dev *tgt_dev;
|
||||
list_for_each_entry(tgt_dev, sess_tgt_dev_list_head,
|
||||
sess_tgt_dev_list_entry) {
|
||||
scst_nexus_loss(tgt_dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
static int scst_abort_all_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd,
|
||||
int nexus_loss)
|
||||
{
|
||||
@@ -4066,7 +4197,7 @@ static int scst_abort_all_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd,
|
||||
sess_tgt_dev_list_entry) {
|
||||
int rc;
|
||||
|
||||
__scst_abort_task_set(mcmd, tgt_dev, 0, 1);
|
||||
__scst_abort_task_set(mcmd, tgt_dev, 0);
|
||||
|
||||
if (nexus_loss)
|
||||
scst_nexus_loss(tgt_dev);
|
||||
@@ -4079,6 +4210,8 @@ static int scst_abort_all_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd,
|
||||
}
|
||||
}
|
||||
|
||||
scst_unblock_aborted_cmds(1);
|
||||
|
||||
mutex_unlock(&scst_mutex);
|
||||
|
||||
res = scst_set_mcmd_next_state(mcmd);
|
||||
@@ -4121,6 +4254,7 @@ static int scst_mgmt_cmd_exec(struct scst_mgmt_cmd *mcmd)
|
||||
break;
|
||||
|
||||
case SCST_NEXUS_LOSS_SESS:
|
||||
case SCST_UNREG_SESS_TM:
|
||||
res = scst_abort_all_nexus_loss_sess(mcmd, 1);
|
||||
break;
|
||||
|
||||
@@ -4155,6 +4289,43 @@ out_done:
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int scst_mgmt_cmd_check_nexus_loss(struct scst_mgmt_cmd *mcmd)
|
||||
{
|
||||
int res;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
mutex_lock(&scst_mutex);
|
||||
|
||||
switch (mcmd->fn) {
|
||||
case SCST_NEXUS_LOSS_SESS:
|
||||
case SCST_UNREG_SESS_TM:
|
||||
scst_do_nexus_loss_sess(mcmd);
|
||||
break;
|
||||
|
||||
case SCST_NEXUS_LOSS:
|
||||
scst_do_nexus_loss_tgt(mcmd);
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&scst_mutex);
|
||||
|
||||
if ((mcmd->fn == SCST_UNREG_SESS_TM) &&
|
||||
(mcmd->sess->unreg_cmds_done_fn != NULL)) {
|
||||
struct scst_session *sess = mcmd->sess;
|
||||
TRACE_MGMT_DBG("Calling unreg_cmds_done_fn(%p)", sess);
|
||||
sess->unreg_cmds_done_fn(sess);
|
||||
TRACE_MGMT_DBG("task_mgmt_all_cmds_done(%p) returned", sess);
|
||||
}
|
||||
|
||||
mcmd->nexus_loss_check_done = 1;
|
||||
|
||||
res = scst_set_mcmd_next_state(mcmd);
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void scst_mgmt_cmd_send_done(struct scst_mgmt_cmd *mcmd)
|
||||
{
|
||||
struct scst_device *dev;
|
||||
@@ -4162,18 +4333,6 @@ static void scst_mgmt_cmd_send_done(struct scst_mgmt_cmd *mcmd)
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
clear_bit(SCST_FLAG_TM_ACTIVE, &scst_flags);
|
||||
spin_lock_irq(&scst_mcmd_lock);
|
||||
if (!list_empty(&scst_delayed_mgmt_cmd_list)) {
|
||||
struct scst_mgmt_cmd *m;
|
||||
m = list_entry(scst_delayed_mgmt_cmd_list.next, typeof(*m),
|
||||
mgmt_cmd_list_entry);
|
||||
TRACE_MGMT_DBG("Moving delayed mgmt cmd %p to head of active "
|
||||
"mgmt cmd list", m);
|
||||
list_move(&m->mgmt_cmd_list_entry, &scst_active_mgmt_cmd_list);
|
||||
}
|
||||
spin_unlock_irq(&scst_mcmd_lock);
|
||||
|
||||
mcmd->state = SCST_MGMT_CMD_STATE_FINISHED;
|
||||
if (scst_is_strict_mgmt_fn(mcmd->fn) && (mcmd->completed_cmd_count > 0))
|
||||
mcmd->status = SCST_MGMT_STATUS_TASK_NOT_EXIST;
|
||||
@@ -4181,7 +4340,8 @@ static void scst_mgmt_cmd_send_done(struct scst_mgmt_cmd *mcmd)
|
||||
TRACE(TRACE_MGMT_MINOR, "TM command fn %d finished, status %x",
|
||||
mcmd->fn, mcmd->status);
|
||||
|
||||
if (mcmd->sess->tgt->tgtt->task_mgmt_fn_done) {
|
||||
if (mcmd->sess->tgt->tgtt->task_mgmt_fn_done &&
|
||||
(mcmd->fn != SCST_UNREG_SESS_TM)) {
|
||||
TRACE_DBG("Calling target %s task_mgmt_fn_done()",
|
||||
mcmd->sess->tgt->tgtt->name);
|
||||
mcmd->sess->tgt->tgtt->task_mgmt_fn_done(mcmd);
|
||||
@@ -4266,6 +4426,11 @@ static int scst_process_mgmt_cmd(struct scst_mgmt_cmd *mcmd)
|
||||
goto out;
|
||||
break;
|
||||
|
||||
case SCST_MGMT_CMD_STATE_CHECK_NEXUS_LOSS:
|
||||
if (scst_mgmt_cmd_check_nexus_loss(mcmd))
|
||||
goto out;
|
||||
break;
|
||||
|
||||
case SCST_MGMT_CMD_STATE_DONE:
|
||||
scst_mgmt_cmd_send_done(mcmd);
|
||||
break;
|
||||
@@ -4383,8 +4548,11 @@ static struct scst_mgmt_cmd *scst_pre_rx_mgmt_cmd(struct scst_session
|
||||
}
|
||||
|
||||
mcmd = scst_alloc_mgmt_cmd(atomic ? GFP_ATOMIC : GFP_KERNEL);
|
||||
if (mcmd == NULL)
|
||||
if (mcmd == NULL) {
|
||||
PRINT_CRIT_ERROR("Lost TM fn %x, initiator %s", fn,
|
||||
sess->initiator_name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
mcmd->sess = sess;
|
||||
mcmd->fn = fn;
|
||||
@@ -4406,19 +4574,17 @@ static int scst_post_rx_mgmt_cmd(struct scst_session *sess,
|
||||
|
||||
scst_sess_get(sess);
|
||||
|
||||
if (unlikely(sess->shut_phase != SCST_SESS_SPH_READY)) {
|
||||
PRINT_ERROR("New mgmt cmd while shutting down the session %p "
|
||||
"shut_phase %ld", sess, sess->shut_phase);
|
||||
sBUG();
|
||||
}
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
spin_lock(&sess->sess_list_lock);
|
||||
atomic_inc(&sess->sess_cmd_count);
|
||||
|
||||
#ifdef EXTRACHECKS
|
||||
if (unlikely(sess->shut_phase != SCST_SESS_SPH_READY)) {
|
||||
PRINT_ERROR("%s",
|
||||
"New mgmt cmd while shutting down the session");
|
||||
sBUG();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (unlikely(sess->init_phase != SCST_SESS_IPH_READY)) {
|
||||
switch(sess->init_phase) {
|
||||
case SCST_SESS_IPH_INITING:
|
||||
@@ -4459,7 +4625,7 @@ out_unlock:
|
||||
}
|
||||
|
||||
/*
|
||||
* Must not be called in parallel with scst_unregister_session() for the
|
||||
* Must not be called in parallel with scst_unregister_session_ex() for the
|
||||
* same sess
|
||||
*/
|
||||
int scst_rx_mgmt_fn(struct scst_session *sess,
|
||||
@@ -4504,9 +4670,10 @@ int scst_rx_mgmt_fn(struct scst_session *sess,
|
||||
"TM fn %x", params->fn);
|
||||
|
||||
TRACE_MGMT_DBG("sess=%p, tag_set %d, tag %Ld, lun_set %d, "
|
||||
"lun=%Ld, cmd_sn_set %d, cmd_sn %d", sess,
|
||||
"lun=%Ld, cmd_sn_set %d, cmd_sn %d, priv %p", sess,
|
||||
params->tag_set, params->tag, params->lun_set,
|
||||
(uint64_t)mcmd->lun, params->cmd_sn_set, params->cmd_sn);
|
||||
(uint64_t)mcmd->lun, params->cmd_sn_set, params->cmd_sn,
|
||||
params->tgt_priv);
|
||||
|
||||
if (scst_post_rx_mgmt_cmd(sess, mcmd) != 0)
|
||||
goto out_free;
|
||||
@@ -4708,14 +4875,16 @@ out_free:
|
||||
* Must not be called in parallel with scst_rx_cmd() or
|
||||
* scst_rx_mgmt_fn_*() for the same sess
|
||||
*/
|
||||
void scst_unregister_session(struct scst_session *sess, int wait,
|
||||
void (*unreg_done_fn) (struct scst_session *sess))
|
||||
void scst_unregister_session_ex(struct scst_session *sess, int wait,
|
||||
void (*unreg_done_fn) (struct scst_session *sess),
|
||||
void (*unreg_cmds_done_fn) (struct scst_session *sess))
|
||||
{
|
||||
unsigned long flags;
|
||||
struct completion *pc;
|
||||
#ifndef CONFIG_LOCKDEP
|
||||
DECLARE_COMPLETION(c);
|
||||
#endif
|
||||
int rc, lun;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
@@ -4727,11 +4896,22 @@ void scst_unregister_session(struct scst_session *sess, int wait,
|
||||
pc = &c;
|
||||
#endif
|
||||
|
||||
sess->unreg_done_fn = unreg_done_fn;
|
||||
sess->unreg_cmds_done_fn = unreg_cmds_done_fn;
|
||||
|
||||
/* Abort all outstanding commands and clear reservation, if necessary */
|
||||
lun = 0;
|
||||
rc = scst_rx_mgmt_fn_lun(sess, SCST_UNREG_SESS_TM,
|
||||
(uint8_t*)&lun, sizeof(lun), SCST_ATOMIC, NULL);
|
||||
if (rc != 0) {
|
||||
PRINT_ERROR("SCST_UNREG_SESS_TM failed %d (sess %p)",
|
||||
rc, sess);
|
||||
}
|
||||
|
||||
sess->shut_phase = SCST_SESS_SPH_PRE_UNREG;
|
||||
|
||||
spin_lock_irqsave(&scst_mgmt_lock, flags);
|
||||
|
||||
sess->unreg_done_fn = unreg_done_fn;
|
||||
if (wait)
|
||||
sess->shutdown_compl = pc;
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
|
||||
Reference in New Issue
Block a user