Merge branch 'svn-trunk'

Conflicts:
	scst/README.dlm
	scst/include/scst.h
	scst/src/dev_handlers/scst_vdisk.c
	scst/src/scst_lib.c
	scst/src/scst_main.c
	scst/src/scst_pres.h
	scst/src/scst_sysfs.c
	scst/src/scst_targ.c
	www/downloads.html
	www/target_lsi.html
This commit is contained in:
Bart Van Assche
2015-11-09 18:27:31 -08:00
91 changed files with 11583 additions and 1186 deletions
+2 -2
View File
@@ -45,7 +45,7 @@ QLA_DIR=qla2x00t_git/qla2x00-target
QLA_OLD_INI_DIR=qla2x00t
QLA_OLD_DIR=qla2x00t/qla2x00-target
LSI_DIR=mpt
USR_DIR=usr/fileio
USR_DIR=usr
SRP_DIR=srpt
SCST_LOCAL_DIR=scst_local
MVSAS_DIR=mvsas_tgt
@@ -129,7 +129,7 @@ help:
@echo " scst_local_install : scst_local target: install"
@echo " scst_local_uninstall : scst_local target: uninstall"
@echo ""
@echo " usr : make user space fileio_tgt target"
@echo " usr : make user space targets"
@echo " usr_clean : usr target: clean "
@echo " usr_extraclean : usr target: clean + clean dependencies"
@echo " usr_install : usr target: install"
+9 -3
View File
@@ -48,9 +48,15 @@ qla2x00t_git
3. Symlink drivers/scsi/qla2xxx subdirectory in the cloned git tree to the
qla2x00t_git subdirectory
Thats all. Now "make all" and other common and QLA specific root
Makefile targets "magically" start working. For detail instructions how
to setup QLA target build environment see its README or HOWTO.
Thats all. Now run either "make all" to build the SCST core and all
target drivers with in-tree QLogic drivers, or the following command
with standalone QLogic drivers:
BUILD_2X_MODULE=y CONFIG_SCSI_QLA_FC=y CONFIG_SCSI_QLA2XXX_TARGET=y make all
and other common and QLA specific root Makefile targets "magically" start
working. For detail instructions how to setup QLA target build environment see
its README or HOWTO.
You can still build the old driver using qla_old* root Makefile targets.
+2
View File
@@ -36,7 +36,9 @@ rtf: $(RTFS)
$(COMMAND)rtf $(<)
clean:
mv "Using the DLM as a Distributed In-Memory Database.pdf" "Using the DLM as a Distributed In-Memory Database.pdf_"
rm -f *.txt *.html *.tex *.dvi *.ps *.pdf *.info *.lyx *.rtf
mv "Using the DLM as a Distributed In-Memory Database.pdf_" "Using the DLM as a Distributed In-Memory Database.pdf"
extraclean: clean
rm -f *.orig *.rej
+1 -1
View File
@@ -26,7 +26,7 @@
#include "scst.h"
#endif
#define FT_VERSION "3.1.0-pre"
#define FT_VERSION "3.2.0-pre"
#define FT_MODULE "fcst"
#define FT_MAX_HW_PENDING_TIME 20 /* max I/O time in seconds */
+1 -1
View File
@@ -1,7 +1,7 @@
iSCSI SCST target driver
========================
Version 3.1.0, XX XXXXX 2014
Version 3.2.0, XX XXXXX 2014
----------------------------
ISCSI-SCST is a deeply reworked fork of iSCSI Enterprise Target (IET)
+1 -1
View File
@@ -21,4 +21,4 @@
#define ISCSI_VERSION_STRING_SUFFIX
#endif
#define ISCSI_VERSION_STRING "3.1.0-pre1" ISCSI_VERSION_STRING_SUFFIX
#define ISCSI_VERSION_STRING "3.2.0-pre1" ISCSI_VERSION_STRING_SUFFIX
+1
View File
@@ -1297,6 +1297,7 @@ unsigned long iscsi_get_flow_ctrl_or_mgmt_dbg_log_flag(struct iscsi_cmnd *cmnd)
flag = TRACE_MGMT_DEBUG;
else {
int status = scst_cmd_get_status(cmnd->scst_cmd);
if ((status == SAM_STAT_TASK_SET_FULL) ||
(status == SAM_STAT_BUSY))
flag = TRACE_FLOW_CONTROL;
+3 -1
View File
@@ -525,6 +525,7 @@ static void conn_rsp_timer_fn(unsigned long arg)
if (!list_empty(&conn->write_timeout_list)) {
unsigned long timeout_time;
cmnd = list_first_entry(&conn->write_timeout_list,
struct iscsi_cmnd, write_timeout_list_entry);
@@ -583,7 +584,7 @@ static void conn_nop_in_delayed_work_fn(struct work_struct *work)
#endif
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
struct iscsi_conn *conn = (struct iscsi_conn *)p;
struct iscsi_conn *conn = p;
#else
struct iscsi_conn *conn = container_of(work, struct iscsi_conn,
nop_in_delayed_work.work);
@@ -834,6 +835,7 @@ void conn_free(struct iscsi_conn *conn)
/* Just in case if new conn gets freed before the old one */
if (test_bit(ISCSI_CONN_REINSTATING, &conn->conn_aflags)) {
struct iscsi_conn *c;
TRACE_MGMT_DBG("Freeing being reinstated conn %p", conn);
list_for_each_entry(c, &session->conn_list,
conn_list_entry) {
+1
View File
@@ -76,6 +76,7 @@ static __be32 evaluate_crc32_from_sg(struct scatterlist *sg, int nbytes,
while (nbytes > 0) {
int d = min(nbytes, (int)(sg->length));
crc = crc32c(crc, sg_virt(sg), d);
nbytes -= d;
sg++;
+13 -8
View File
@@ -138,6 +138,7 @@ static inline int cmnd_read_size(struct iscsi_cmnd *cmnd)
if (ahdr != NULL) {
uint8_t *p = (uint8_t *)ahdr;
unsigned int size = 0;
do {
int s;
@@ -477,6 +478,7 @@ void cmnd_done(struct iscsi_cmnd *cmnd)
{
/* It can be for some aborted commands */
struct scst_cmd *scst_cmd = cmnd->scst_cmd;
TRACE_DBG("cmd %p AFTER_PREPROC", cmnd);
cmnd->scst_state = ISCSI_CMD_STATE_RESTARTED;
cmnd->scst_cmd = NULL;
@@ -1095,6 +1097,7 @@ static void iscsi_tcp_send_data_rsp(struct iscsi_cmnd *req, u8 *sense,
send_data_rsp(req, status, is_send_status);
} else {
struct iscsi_cmnd *rsp;
send_data_rsp(req, 0, 0);
if (is_send_status) {
rsp = create_status_rsp(req, status, sense,
@@ -1229,6 +1232,7 @@ static int create_reject_rsp(struct iscsi_cmnd *req, int reason, bool get_data)
if (req->scst_cmd == NULL) {
/* BUSY status must be already set */
struct iscsi_scsi_rsp_hdr *rsp_hdr1;
rsp_hdr1 = (struct iscsi_scsi_rsp_hdr *)&req->main_rsp->pdu.bhs;
sBUG_ON(rsp_hdr1->cmd_status == 0);
/*
@@ -1531,6 +1535,7 @@ int iscsi_preliminary_complete(struct iscsi_cmnd *req,
#ifdef CONFIG_SCST_DEBUG
{
struct iscsi_hdr *req_hdr = &req->pdu.bhs;
TRACE_DBG_FLAG(iscsi_get_flow_ctrl_or_mgmt_dbg_log_flag(orig_req),
"Prelim completed req %p, orig_req %p (FINAL %x, "
"outstanding_r2t %d)", req, orig_req,
@@ -1748,8 +1753,7 @@ out:
static int iscsi_pre_exec(struct scst_cmd *scst_cmd)
{
int res = SCST_PREPROCESS_STATUS_SUCCESS;
struct iscsi_cmnd *req = (struct iscsi_cmnd *)
scst_cmd_get_tgt_priv(scst_cmd);
struct iscsi_cmnd *req = scst_cmd_get_tgt_priv(scst_cmd);
struct iscsi_cmnd *c, *t;
TRACE_ENTRY();
@@ -2055,6 +2059,7 @@ static int scsi_cmnd_start(struct iscsi_cmnd *req)
if ((req_hdr->flags & ISCSI_CMD_READ) &&
(req_hdr->flags & ISCSI_CMD_WRITE)) {
int sz = cmnd_read_size(req);
if (unlikely(sz < 0)) {
PRINT_ERROR("%s", "BIDI data transfer, but initiator "
"not supplied Bidirectional Read Expected Data "
@@ -2117,6 +2122,7 @@ static int scsi_cmnd_start(struct iscsi_cmnd *req)
if (ahdr != NULL) {
uint8_t *p = (uint8_t *)ahdr;
unsigned int size = 0;
do {
int s;
@@ -2517,6 +2523,7 @@ static void iscsi_cmnd_abort_fn(struct work_struct *work)
*/
list_for_each_entry(conn, &session->conn_list, conn_list_entry) {
struct iscsi_cmnd *c;
spin_lock_bh(&conn->cmd_list_lock);
list_for_each_entry(c, &conn->cmd_list, cmd_list_entry) {
if (c == cmnd) {
@@ -3376,8 +3383,7 @@ static void iscsi_tcp_preprocessing_done(struct iscsi_cmnd *req)
static void iscsi_preprocessing_done(struct scst_cmd *scst_cmd)
{
struct iscsi_cmnd *req = (struct iscsi_cmnd *)
scst_cmd_get_tgt_priv(scst_cmd);
struct iscsi_cmnd *req = scst_cmd_get_tgt_priv(scst_cmd);
req->conn->transport->iscsit_preprocessing_done(req);
}
@@ -3489,8 +3495,7 @@ static void iscsi_tcp_conn_close(struct iscsi_conn *conn, int flags)
static int iscsi_xmit_response(struct scst_cmd *scst_cmd)
{
int is_send_status = scst_cmd_get_is_send_status(scst_cmd);
struct iscsi_cmnd *req = (struct iscsi_cmnd *)
scst_cmd_get_tgt_priv(scst_cmd);
struct iscsi_cmnd *req = scst_cmd_get_tgt_priv(scst_cmd);
struct iscsi_conn *conn = req->conn;
int status = scst_cmd_get_status(scst_cmd);
u8 *sense = scst_cmd_get_sense_buffer(scst_cmd);
@@ -3574,6 +3579,7 @@ static int iscsi_xmit_response(struct scst_cmd *scst_cmd)
is_send_status);
} else if (is_send_status) {
struct iscsi_cmnd *rsp;
rsp = create_status_rsp(req, status, sense, sense_len);
iscsi_cmnd_init_write(rsp, 0);
}
@@ -3768,8 +3774,7 @@ static inline int iscsi_get_mgmt_response(int status)
static void iscsi_task_mgmt_fn_done(struct scst_mgmt_cmd *scst_mcmd)
{
int fn = scst_mgmt_cmd_get_fn(scst_mcmd);
struct iscsi_cmnd *req = (struct iscsi_cmnd *)
scst_mgmt_cmd_get_tgt_priv(scst_mcmd);
struct iscsi_cmnd *req = scst_mgmt_cmd_get_tgt_priv(scst_mcmd);
int status = iscsi_get_mgmt_response(scst_mgmt_cmd_get_status(scst_mcmd));
if ((status == ISCSI_RESPONSE_UNKNOWN_TASK) &&
+1
View File
@@ -693,6 +693,7 @@ static inline bool cmnd_get_check(struct iscsi_cmnd *cmnd)
{
int r = atomic_inc_return(&cmnd->ref_cnt);
int res;
if (unlikely(r == 1)) {
TRACE_DBG("cmnd %p is being destroyed", cmnd);
atomic_dec(&cmnd->ref_cnt);
+1 -1
View File
@@ -284,7 +284,7 @@ void isert_conn_free(struct isert_connection *isert_conn);
void isert_conn_disconnect(struct isert_connection *isert_conn);
void isert_post_drain(struct isert_connection *isert_conn);
static inline struct isert_connection *isert_conn_alloc(void)
static inline struct isert_connection *isert_conn_zalloc(void)
{
return kmem_cache_zalloc(isert_conn_cache, GFP_KERNEL);
}
+3 -2
View File
@@ -49,8 +49,8 @@ static int isert_buf_alloc_pg(struct ib_device *ib_dev,
struct page *page;
isert_buf->sg_cnt = DIV_ROUND_UP(size, PAGE_SIZE);
isert_buf->sg = kmalloc(sizeof(*isert_buf->sg) * isert_buf->sg_cnt,
GFP_KERNEL);
isert_buf->sg = kmalloc_array(isert_buf->sg_cnt, sizeof(*isert_buf->sg),
GFP_KERNEL);
if (unlikely(!isert_buf->sg)) {
pr_err("Failed to allocate buffer SG\n");
res = -ENOMEM;
@@ -294,6 +294,7 @@ out:
void isert_wr_release(struct isert_wr *wr)
{
struct isert_buf *isert_buf = wr->buf;
if (isert_buf && isert_buf->is_alloced) {
struct isert_device *isert_dev = wr->isert_dev;
struct ib_device *ib_dev;
+42 -22
View File
@@ -63,7 +63,8 @@ int isert_get_peer_addr(struct iscsi_conn *iscsi_conn, struct sockaddr *sa,
size_t *addr_len)
{
int ret;
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
struct isert_connection *isert_conn = container_of(iscsi_conn,
struct isert_connection, iscsi);
struct sockaddr *peer_sa = (struct sockaddr *)&isert_conn->peer_addr;
ret = isert_get_addr_size(peer_sa, addr_len);
@@ -79,7 +80,8 @@ int isert_get_target_addr(struct iscsi_conn *iscsi_conn, struct sockaddr *sa,
size_t *addr_len)
{
int ret;
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
struct isert_connection *isert_conn = container_of(iscsi_conn,
struct isert_connection, iscsi);
struct sockaddr *self_sa = (struct sockaddr *)&isert_conn->self_addr;
ret = isert_get_addr_size(self_sa, addr_len);
@@ -106,14 +108,17 @@ int isert_portal_remove(void *portal_h)
void isert_free_connection(struct iscsi_conn *iscsi_conn)
{
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
struct isert_connection *isert_conn = container_of(iscsi_conn,
struct isert_connection, iscsi);
isert_post_drain(isert_conn);
isert_conn_free(isert_conn);
}
struct iscsi_cmnd *isert_alloc_login_rsp_pdu(struct iscsi_conn *iscsi_conn)
{
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
struct isert_connection *isert_conn = container_of(iscsi_conn,
struct isert_connection, iscsi);
struct isert_cmnd *isert_pdu = isert_conn->login_rsp_pdu;
isert_tx_pdu_init(isert_pdu, isert_conn);
@@ -123,7 +128,8 @@ struct iscsi_cmnd *isert_alloc_login_rsp_pdu(struct iscsi_conn *iscsi_conn)
static struct iscsi_cmnd *isert_alloc_scsi_pdu(struct iscsi_conn *iscsi_conn,
int fake)
{
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
struct isert_connection *isert_conn = container_of(iscsi_conn,
struct isert_connection, iscsi);
struct isert_cmnd *isert_pdu;
again:
@@ -153,8 +159,10 @@ struct iscsi_cmnd *isert_alloc_scsi_fake_pdu(struct iscsi_conn *iscsi_conn)
void isert_release_tx_pdu(struct iscsi_cmnd *iscsi_pdu)
{
struct isert_cmnd *isert_pdu = (struct isert_cmnd *)iscsi_pdu;
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_pdu->conn;
struct isert_cmnd *isert_pdu = container_of(iscsi_pdu,
struct isert_cmnd, iscsi);
struct isert_connection *isert_conn = container_of(iscsi_pdu->conn,
struct isert_connection, iscsi);
isert_tx_pdu_init_iscsi(isert_pdu);
@@ -165,16 +173,17 @@ void isert_release_tx_pdu(struct iscsi_cmnd *iscsi_pdu)
void isert_release_rx_pdu(struct iscsi_cmnd *iscsi_pdu)
{
struct isert_cmnd *isert_pdu = (struct isert_cmnd *)iscsi_pdu;
struct isert_cmnd *isert_pdu = container_of(iscsi_pdu,
struct isert_cmnd, iscsi);
if (likely(!isert_pdu->is_fake_rx))
isert_rx_pdu_done(isert_pdu);
isert_rx_pdu_done(isert_pdu);
}
/* if last transition into FF (Fully Featured) state */
int isert_login_rsp_tx(struct iscsi_cmnd *login_rsp, int last, int discovery)
{
struct isert_connection *isert_conn = (struct isert_connection *)login_rsp->conn;
struct isert_connection *isert_conn = container_of(login_rsp->conn,
struct isert_connection, iscsi);
int err;
if (last && !discovery) {
@@ -202,7 +211,8 @@ int isert_set_session_params(struct iscsi_conn *iscsi_conn,
struct iscsi_sess_params *sess_params,
struct iscsi_tgt_params *tgt_params)
{
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
struct isert_connection *isert_conn = container_of(iscsi_conn,
struct isert_connection, iscsi);
isert_conn->queue_depth = tgt_params->queued_cmnds;
@@ -217,8 +227,10 @@ int isert_set_session_params(struct iscsi_conn *iscsi_conn,
int isert_pdu_tx(struct iscsi_cmnd *iscsi_cmnd)
{
struct isert_cmnd *isert_cmnd = (struct isert_cmnd *)iscsi_cmnd;
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_cmnd->conn;
struct isert_cmnd *isert_cmnd = container_of(iscsi_cmnd,
struct isert_cmnd, iscsi);
struct isert_connection *isert_conn = container_of(iscsi_cmnd->conn,
struct isert_connection, iscsi);
int err;
isert_tx_pdu_convert_from_iscsi(isert_cmnd, iscsi_cmnd);
@@ -229,8 +241,10 @@ int isert_pdu_tx(struct iscsi_cmnd *iscsi_cmnd)
int isert_request_data_out(struct iscsi_cmnd *iscsi_cmnd)
{
struct isert_cmnd *isert_cmnd = (struct isert_cmnd *)iscsi_cmnd;
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_cmnd->conn;
struct isert_cmnd *isert_cmnd = container_of(iscsi_cmnd,
struct isert_cmnd, iscsi);
struct isert_connection *isert_conn = container_of(iscsi_cmnd->conn,
struct isert_connection, iscsi);
int ret;
ret = isert_prepare_rdma(isert_cmnd, isert_conn, ISER_WR_RDMA_READ);
@@ -245,9 +259,12 @@ int isert_request_data_out(struct iscsi_cmnd *iscsi_cmnd)
int isert_send_data_in(struct iscsi_cmnd *iscsi_cmnd,
struct iscsi_cmnd *iscsi_rsp)
{
struct isert_cmnd *isert_cmnd = (struct isert_cmnd *)iscsi_cmnd;
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_cmnd->conn;
struct isert_cmnd *isert_rsp = (struct isert_cmnd *)iscsi_rsp;
struct isert_cmnd *isert_cmnd = container_of(iscsi_cmnd,
struct isert_cmnd, iscsi);
struct isert_connection *isert_conn = container_of(iscsi_cmnd->conn,
struct isert_connection, iscsi);
struct isert_cmnd *isert_rsp = container_of(iscsi_rsp,
struct isert_cmnd, iscsi);
int ret;
ret = isert_prepare_rdma(isert_cmnd, isert_conn, ISER_WR_RDMA_WRITE);
@@ -262,7 +279,8 @@ int isert_send_data_in(struct iscsi_cmnd *iscsi_cmnd,
int isert_close_connection(struct iscsi_conn *iscsi_conn)
{
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
struct isert_connection *isert_conn = container_of(iscsi_conn,
struct isert_connection, iscsi);
isert_conn_disconnect(isert_conn);
@@ -276,14 +294,16 @@ int isert_task_abort(struct iscsi_cmnd *cmnd)
void *isert_get_priv(struct iscsi_conn *iscsi_conn)
{
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
struct isert_connection *isert_conn = container_of(iscsi_conn,
struct isert_connection, iscsi);
return isert_conn->priv_data;
}
void isert_set_priv(struct iscsi_conn *iscsi_conn, void *priv)
{
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
struct isert_connection *isert_conn = container_of(iscsi_conn,
struct isert_connection, iscsi);
isert_conn->priv_data = priv;
}
+7 -4
View File
@@ -82,6 +82,7 @@ static int isert_rx_pdu_init(struct isert_cmnd *isert_pdu,
{
struct iscsi_cmnd *iscsi_cmnd = &isert_pdu->iscsi;
int err = isert_pdu_rx_buf_init(isert_pdu, isert_conn);
if (unlikely(err < 0))
return err;
iscsi_cmnd->conn = &isert_conn->iscsi;
@@ -174,14 +175,14 @@ static int isert_alloc_for_rdma(struct isert_cmnd *pdu, int sge_cnt,
int i, ret = 0;
int wr_cnt;
sg_pool = kmalloc(sizeof(*sg_pool) * sge_cnt, GFP_KERNEL);
sg_pool = kmalloc_array(sge_cnt, sizeof(*sg_pool), GFP_KERNEL);
if (unlikely(sg_pool == NULL)) {
ret = -ENOMEM;
goto out;
}
wr_cnt = DIV_ROUND_UP(sge_cnt, isert_conn->max_sge);
wr = kmalloc(sizeof(*wr) * wr_cnt, GFP_KERNEL);
wr = kmalloc_array(wr_cnt, sizeof(*wr), GFP_KERNEL);
if (unlikely(wr == NULL)) {
ret = -ENOMEM;
goto out_free_sg_pool;
@@ -478,7 +479,8 @@ clean_pdus:
static int isert_reinit_rx_pdu(struct isert_cmnd *pdu)
{
struct isert_connection *isert_conn = (struct isert_connection *)pdu->iscsi.conn;
struct isert_connection *isert_conn = container_of(pdu->iscsi.conn,
struct isert_connection, iscsi);
pdu->is_rstag_valid = 0;
pdu->is_wstag_valid = 0;
@@ -491,7 +493,8 @@ static int isert_reinit_rx_pdu(struct isert_cmnd *pdu)
int isert_rx_pdu_done(struct isert_cmnd *pdu)
{
int err;
struct isert_connection *isert_conn = (struct isert_connection *)pdu->iscsi.conn;
struct isert_connection *isert_conn = container_of(pdu->iscsi.conn,
struct isert_connection, iscsi);
TRACE_ENTRY();
+15 -8
View File
@@ -136,8 +136,10 @@ void isert_post_drain(struct isert_connection *isert_conn)
err = ib_post_send(isert_conn->qp, &isert_conn->drain_wr.send_wr, &bad_wr);
if (unlikely(err)) {
pr_err("Failed to post drain wr, err:%d\n", err);
/* We need to decrement iser_conn->kref in order to be able to cleanup
* the connection */
/*
* We need to decrement iser_conn->kref in order to be
* able to cleanup the connection.
*/
set_bit(ISERT_DRAIN_FAILED, &isert_conn->flags);
isert_conn_free(isert_conn);
}
@@ -352,7 +354,9 @@ static void isert_send_completion_handler(struct isert_wr *wr)
struct isert_cmnd *isert_pdu = wr->pdu;
struct iscsi_cmnd *iscsi_pdu = &isert_pdu->iscsi;
struct iscsi_cmnd *iscsi_req_pdu = iscsi_pdu->parent_req;
struct isert_cmnd *isert_req_pdu = (struct isert_cmnd *)iscsi_req_pdu;
struct isert_cmnd *isert_req_pdu = container_of(iscsi_req_pdu,
struct isert_cmnd, iscsi);
TRACE_ENTRY();
@@ -633,9 +637,11 @@ static void isert_handle_wc_error(struct ib_wc *wc)
isert_buf->dma_dir);
isert_buf->sg_cnt = 0;
}
/* RDMA-WR and SEND response of a READ task
are sent together, so when receiving RDMA-WR error,
wait until SEND error arrives to complete the task */
/*
* RDMA-WR and SEND response of a READ task
* are sent together, so when receiving RDMA-WR error,
* wait until SEND error arrives to complete the task.
*/
break;
default:
pr_err("unexpected opcode %d, wc:%p wr_id:%p conn:%p\n",
@@ -866,7 +872,8 @@ static struct isert_device *isert_device_create(struct ib_device *ib_dev)
isert_dev->num_cqs = min_t(int, num_online_cpus(),
ib_dev->num_comp_vectors);
isert_dev->cq_qps = kzalloc(sizeof(*isert_dev->cq_qps) * isert_dev->num_cqs,
isert_dev->cq_qps = kcalloc(isert_dev->num_cqs,
sizeof(*isert_dev->cq_qps),
GFP_KERNEL);
if (unlikely(isert_dev->cq_qps == NULL)) {
pr_err("Failed to allocate iser cq_qps\n");
@@ -1142,7 +1149,7 @@ static struct isert_connection *isert_conn_create(struct rdma_cm_id *cm_id,
TRACE_ENTRY();
isert_conn = isert_conn_alloc();
isert_conn = isert_conn_zalloc();
if (unlikely(!isert_conn)) {
pr_err("Unable to allocate iser conn, cm_id:%p\n", cm_id);
err = -ENOMEM;
+10 -4
View File
@@ -46,6 +46,7 @@
#endif
#include "isert_dbg.h"
#include "isert.h"
#include "iser.h"
#include "iser_datamover.h"
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
@@ -190,6 +191,9 @@ static struct iscsi_cmnd *isert_cmnd_alloc(struct iscsi_conn *conn,
static void isert_cmnd_free(struct iscsi_cmnd *cmnd)
{
struct isert_cmnd *isert_cmnd = container_of(cmnd, struct isert_cmnd,
iscsi);
TRACE_ENTRY();
#ifdef CONFIG_SCST_EXTRACHECKS
@@ -211,7 +215,7 @@ static void isert_cmnd_free(struct iscsi_cmnd *cmnd)
sBUG();
}
#endif
if (cmnd->parent_req)
if (cmnd->parent_req || isert_cmnd->is_fake_rx)
isert_release_tx_pdu(cmnd);
else
isert_release_rx_pdu(cmnd);
@@ -282,9 +286,11 @@ static void isert_free_conn(struct iscsi_conn *conn)
int isert_handle_close_connection(struct iscsi_conn *conn)
{
isert_mark_conn_closed(conn, 0);
/* Take care of case where our connection is being closed
* without being connected to a session - if connection allocation
* failed for some reason */
/*
* Take care of case where our connection is being closed without
* being connected to a session - if connection allocation failed for
* some reason.
*/
if (unlikely(!conn->session))
isert_free_connection(conn);
else
+3 -7
View File
@@ -567,6 +567,7 @@ static ssize_t isert_read(struct file *filp, char __user *buf, size_t count,
if (will_read_block(dev)) {
int ret;
if (filp->f_flags & O_NONBLOCK)
return -EAGAIN;
ret = wait_event_freezable(dev->waitqueue,
@@ -804,11 +805,8 @@ int isert_login_req_rx(struct iscsi_cmnd *login_req)
switch (dev->state) {
case CS_INIT:
case CS_RSP_FINISHED:
if (unlikely(dev->login_req != NULL)) {
if (unlikely(dev->login_req != NULL))
sBUG();
res = -EINVAL;
goto out;
}
break;
case CS_REQ_BHS: /* Got login request before done handling old one */
@@ -825,8 +823,6 @@ int isert_login_req_rx(struct iscsi_cmnd *login_req)
default:
sBUG();
res = -EINVAL;
goto out;
}
@@ -956,7 +952,7 @@ int __init isert_init_login_devs(unsigned int ndevs)
* allocate the devices -- we can't have them static, as the number
* can be specified at load time
*/
isert_conn_devices = kzalloc(n_devs * sizeof(struct isert_conn_dev),
isert_conn_devices = kcalloc(n_devs, sizeof(struct isert_conn_dev),
GFP_KERNEL);
if (unlikely(!isert_conn_devices)) {
res = -ENOMEM;
+18 -4
View File
@@ -100,6 +100,7 @@ again:
for (i = 0; i < cmnd->sg_cnt; i++) {
struct page *page = sg_page(&cmnd->sg[i]);
TRACE_CONN_CLOSE_DBG("page %p, net_priv %p, "
"_count %d", page, page->net_priv,
atomic_read(&page->_count));
@@ -282,8 +283,10 @@ static void trace_conn_close(struct iscsi_conn *conn)
cmnd->sg);
if (cmnd->sg != NULL) {
int i;
for (i = 0; i < cmnd->sg_cnt; i++) {
struct page *page = sg_page(&cmnd->sg[i]);
TRACE_CONN_CLOSE_DBG("page %p, "
"net_priv %p, _count %d",
page, page->net_priv,
@@ -301,6 +304,7 @@ static void trace_conn_close(struct iscsi_conn *conn)
atomic_read(&rsp->net_ref_cnt), rsp->sg);
if (rsp->sg != cmnd->sg && rsp->sg) {
int i;
for (i = 0; i < rsp->sg_cnt; i++) {
TRACE_CONN_CLOSE_DBG(" page %p, "
"net_priv %p, _count %d",
@@ -330,7 +334,7 @@ void iscsi_task_mgmt_affected_cmds_done(struct scst_mgmt_cmd *scst_mcmd)
switch (fn) {
case SCST_NEXUS_LOSS_SESS:
{
struct iscsi_conn *conn = (struct iscsi_conn *)priv;
struct iscsi_conn *conn = priv;
struct iscsi_session *sess = conn->session;
struct iscsi_conn *c;
@@ -459,6 +463,7 @@ static void close_conn(struct iscsi_conn *conn)
spin_lock(&session->sn_lock);
if (session->tm_rsp && session->tm_rsp->conn == conn) {
struct iscsi_cmnd *tm_rsp = session->tm_rsp;
iscsi_drop_delayed_tm_rsp(tm_rsp);
spin_unlock(&session->sn_lock);
mutex_unlock(&target->target_mutex);
@@ -579,7 +584,7 @@ static void close_conn(struct iscsi_conn *conn)
static int close_conn_thr(void *arg)
{
struct iscsi_conn *conn = (struct iscsi_conn *)arg;
struct iscsi_conn *conn = arg;
TRACE_ENTRY();
@@ -789,6 +794,7 @@ static int iscsi_rx_check_ddigest(struct iscsi_conn *conn)
res = digest_rx_data(cmnd);
if (unlikely(res != 0)) {
struct iscsi_cmnd *orig_req;
if (cmnd_opcode(cmnd) == ISCSI_OP_SCSI_DATA_OUT)
orig_req = cmnd->cmd_req;
else
@@ -908,6 +914,7 @@ static int process_read_io(struct iscsi_conn *conn, int *closed)
res = do_recv(conn);
if (res == 0) {
int psz = ((cmnd->pdu.datasize + 3) & -4) - cmnd->pdu.datasize;
if (psz != 0) {
TRACE_DBG("padding %d bytes", psz);
iscsi_conn_init_read(conn,
@@ -1132,7 +1139,7 @@ static inline void __iscsi_get_page_callback(struct iscsi_cmnd *cmd)
void iscsi_get_page_callback(struct page *page)
{
struct iscsi_cmnd *cmd = (struct iscsi_cmnd *)page->net_priv;
struct iscsi_cmnd *cmd = page->net_priv;
TRACE_NET_PAGE("page %p, _count %d", page,
atomic_read(&page->_count));
@@ -1148,8 +1155,10 @@ static inline void __iscsi_put_page_callback(struct iscsi_cmnd *cmd)
if (atomic_dec_and_test(&cmd->net_ref_cnt)) {
int i, sg_cnt = cmd->sg_cnt;
for (i = 0; i < sg_cnt; i++) {
struct page *page = sg_page(&cmd->sg[i]);
TRACE_NET_PAGE("Clearing page %p", page);
if (page->net_priv == cmd)
page->net_priv = NULL;
@@ -1161,7 +1170,7 @@ static inline void __iscsi_put_page_callback(struct iscsi_cmnd *cmd)
void iscsi_put_page_callback(struct page *page)
{
struct iscsi_cmnd *cmd = (struct iscsi_cmnd *)page->net_priv;
struct iscsi_cmnd *cmd = page->net_priv;
TRACE_NET_PAGE("page %p, _count %d", page,
atomic_read(&page->_count));
@@ -1216,9 +1225,11 @@ void req_add_to_write_timeout_list(struct iscsi_cmnd *req)
unsigned long req_tt = iscsi_get_timeout_time(req);
struct iscsi_cmnd *r;
bool inserted = false;
list_for_each_entry(r, &conn->write_timeout_list,
write_timeout_list_entry) {
unsigned long tt = iscsi_get_timeout_time(r);
if (time_after(tt, req_tt)) {
TRACE_DBG("Add NOP IN req %p (tt %ld) before "
"req %p (tt %ld)", req, req_tt, r, tt);
@@ -1250,6 +1261,7 @@ void req_add_to_write_timeout_list(struct iscsi_cmnd *req)
if (!timer_pending(&conn->rsp_timer)) {
unsigned long timeout_time;
if (unlikely(conn->conn_tm_active ||
test_bit(ISCSI_CMD_ABORTED,
&req->prelim_compl_flags))) {
@@ -1270,6 +1282,7 @@ void req_add_to_write_timeout_list(struct iscsi_cmnd *req)
&req->prelim_compl_flags))) {
unsigned long timeout_time = jiffies +
ISCSI_TM_DATA_WAIT_TIMEOUT + ISCSI_ADD_SCHED_TIME;
set_conn_tm_active = true;
if (time_after(conn->rsp_timer.expires, timeout_time)) {
TRACE_MGMT_DBG("Mod timer on %ld (conn %p)",
@@ -1448,6 +1461,7 @@ retry:
#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION)
{
static DEFINE_SPINLOCK(net_priv_lock);
spin_lock(&net_priv_lock);
if (unlikely(page->net_priv != NULL)) {
if (page->net_priv != ref_cmd) {
+2
View File
@@ -199,6 +199,7 @@ int __add_session(struct iscsi_target *target,
list_for_each_entry_reverse(sess, &target->session_list,
session_list_entry) {
union iscsi_sid s = *(union iscsi_sid *)&sess->sid;
s.id.tsih = 0;
if ((sid.id64 == s.id64) &&
(strcmp(info->initiator_name, sess->initiator_name) == 0)) {
@@ -332,6 +333,7 @@ int session_free(struct iscsi_session *session, bool del)
if (session->sess_reinstating) {
struct iscsi_session *s;
TRACE_MGMT_DBG("Freeing being reinstated sess %p", session);
list_for_each_entry(s, &session->target->session_list,
session_list_entry) {
+2
View File
@@ -298,6 +298,7 @@ void target_del_session(struct iscsi_target *target,
if (!list_empty(&session->conn_list)) {
struct iscsi_conn *conn, *tc;
list_for_each_entry_safe(conn, tc, &session->conn_list,
conn_list_entry) {
TRACE_MGMT_DBG("Del session: closing conn %p", conn);
@@ -616,6 +617,7 @@ ssize_t iscsi_sysfs_del_target(const char *target_name)
/* We don't want to have tgt visible after the mutex unlock */
{
struct iscsi_target *tgt;
mutex_lock(&target_mgmt_mutex);
tgt = target_lookup_by_name(target_name);
if (tgt == NULL) {
+3 -2
View File
@@ -3,8 +3,9 @@
ABT_DETAILS="x86_64"
ABT_JOBS=5
ABT_KERNELS=" \
4.2.5 \
4.1.12-nc \
4.3 \
4.2.5-nc \
4.1.12-nc \
4.0.9-nc \
3.19.7-nc \
3.18.19-nc \
@@ -0,0 +1,5 @@
ccflags-y += -Idrivers/scsi/qla2xxx
qla2x00tgt-y := qla2x00t.o
obj-$(CONFIG_SCST_QLA_TGT_ADDON) += qla2x00tgt.o
+2 -2
View File
@@ -1,7 +1,7 @@
Target driver for QLogic 2[2-6]xx/8[1-3]xx Fibre Channel cards
==============================================================
Version 3.1.0, XX XXXXX 2015
Version 3.2.0, XX XXXXX 2015
----------------------------
This is target driver for QLogic 2[2-6]xx/8[1-3]xx Fibre Channel cards.
@@ -20,7 +20,7 @@ necessary callbacks, but it's still capable to work as initiator only.
Mode, when a host acts as the initiator and the target simultaneously,
is supported as well.
This version is compatible with SCST core version 3.1.0 and higher and
This version is compatible with SCST core version 3.2.0 and higher and
Linux kernel 2.6.26 and higher. Sorry, kernels below 2.6.26 are not
supported, because it's too hard to backport used initiator driver to
older kernels.
+65 -3
View File
@@ -308,6 +308,7 @@ static inline struct q2t_sess *q2t_find_sess_by_loop_id(struct q2t_tgt *tgt,
uint16_t loop_id)
{
struct q2t_sess *sess;
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
if (loop_id == sess->loop_id) {
EXTRACHECKS_BUG_ON(sess->deleted);
@@ -322,6 +323,7 @@ static inline struct q2t_sess *q2t_find_sess_by_s_id_include_deleted(
struct q2t_tgt *tgt, const uint8_t *s_id)
{
struct q2t_sess *sess;
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
if ((sess->s_id.b.al_pa == s_id[2]) &&
(sess->s_id.b.area == s_id[1]) &&
@@ -346,6 +348,7 @@ static inline struct q2t_sess *q2t_find_sess_by_s_id(struct q2t_tgt *tgt,
const uint8_t *s_id)
{
struct q2t_sess *sess;
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
if ((sess->s_id.b.al_pa == s_id[2]) &&
(sess->s_id.b.area == s_id[1]) &&
@@ -362,6 +365,7 @@ static inline struct q2t_sess *q2t_find_sess_by_s_id_le(struct q2t_tgt *tgt,
const uint8_t *s_id)
{
struct q2t_sess *sess;
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
if ((sess->s_id.b.al_pa == s_id[0]) &&
(sess->s_id.b.area == s_id[1]) &&
@@ -378,6 +382,7 @@ static inline struct q2t_sess *q2t_find_sess_by_port_name(struct q2t_tgt *tgt,
const uint8_t *port_name)
{
struct q2t_sess *sess;
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
if ((sess->port_name[0] == port_name[0]) &&
(sess->port_name[1] == port_name[1]) &&
@@ -399,6 +404,7 @@ static inline struct q2t_sess *q2t_find_sess_by_port_name_include_deleted(
struct q2t_tgt *tgt, const uint8_t *port_name)
{
struct q2t_sess *sess;
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
if ((sess->port_name[0] == port_name[0]) &&
(sess->port_name[1] == port_name[1]) &&
@@ -446,6 +452,7 @@ static inline int q2t_issue_marker(scsi_qla_host_t *vha, int vha_locked)
/* Send marker if required */
if (unlikely(vha->marker_needed != 0)) {
int rc = qla2x00_issue_marker(vha, vha_locked);
if (rc != QLA_SUCCESS) {
PRINT_ERROR("qla2x00t(%ld): issue_marker() "
"failed", vha->host_no);
@@ -468,6 +475,7 @@ static inline scsi_qla_host_t *q2t_find_host_by_d_id(scsi_qla_host_t *vha,
if (IS_FWI2_CAPABLE(ha)) {
uint8_t vp_idx;
sBUG_ON(ha->tgt_vp_map == NULL);
vp_idx = ha->tgt_vp_map[d_id[2]].idx;
if (likely(test_bit(vp_idx, ha->vp_idx_map)))
@@ -547,6 +555,7 @@ static void q24_try_to_dequeue_unknown_atios(struct qla_hw_data *ha)
list_for_each_entry_safe(u, t, &ha->unknown_atio_list,
unknown_atio_list_entry) {
scsi_qla_host_t *host, *vha = u->vha;
sBUG_ON(vha->hw != ha);
host = q2t_find_host_by_d_id(vha, u->atio7.fcp_hdr.d_id);
if (host != NULL) {
@@ -610,7 +619,8 @@ static void q24_atio_pkt_all_vps(scsi_qla_host_t *vha, atio7_entry_t *atio)
case ATIO_TYPE7:
{
scsi_qla_host_t *host = q2t_find_host_by_d_id(vha, atio->fcp_hdr.d_id);
if (unlikely(NULL == host)) {
if (unlikely(host == NULL)) {
/*
* It might happen, because there is a small gap between
* requesting the DPC thread to update loop and actual
@@ -628,8 +638,10 @@ static void q24_atio_pkt_all_vps(scsi_qla_host_t *vha, atio7_entry_t *atio)
case IMMED_NOTIFY_TYPE:
{
scsi_qla_host_t *host = vha;
if (IS_FWI2_CAPABLE(ha)) {
notify24xx_entry_t *entry = (notify24xx_entry_t *)atio;
if ((entry->vp_index != 0xFF) &&
(entry->nport_handle != 0xFFFF)) {
host = q2t_find_host_by_vp_idx(vha,
@@ -671,6 +683,7 @@ static void q2t_response_pkt_all_vps(scsi_qla_host_t *vha, response_t *pkt)
ctio7_fw_entry_t *entry = (ctio7_fw_entry_t *)pkt;
scsi_qla_host_t *host = q2t_find_host_by_vp_idx(vha,
entry->vp_index);
if (unlikely(!host)) {
PRINT_ERROR("qla2x00t(%ld): Response pkt (CTIO_TYPE7) "
"received, with unknown vp_index %d",
@@ -684,8 +697,10 @@ static void q2t_response_pkt_all_vps(scsi_qla_host_t *vha, response_t *pkt)
case IMMED_NOTIFY_TYPE:
{
scsi_qla_host_t *host = vha;
if (IS_FWI2_CAPABLE(ha)) {
notify24xx_entry_t *entry = (notify24xx_entry_t *)pkt;
host = q2t_find_host_by_vp_idx(vha, entry->vp_index);
if (unlikely(!host)) {
PRINT_ERROR("qla2x00t(%ld): Response pkt "
@@ -702,9 +717,11 @@ static void q2t_response_pkt_all_vps(scsi_qla_host_t *vha, response_t *pkt)
case NOTIFY_ACK_TYPE:
{
scsi_qla_host_t *host = vha;
if (IS_FWI2_CAPABLE(ha)) {
nack24xx_entry_t *entry = (nack24xx_entry_t *)pkt;
if (0xFF != entry->vp_index) {
if (entry->vp_index != 0xFF) {
host = q2t_find_host_by_vp_idx(vha,
entry->vp_index);
if (unlikely(!host)) {
@@ -726,6 +743,7 @@ static void q2t_response_pkt_all_vps(scsi_qla_host_t *vha, response_t *pkt)
abts24_recv_entry_t *entry = (abts24_recv_entry_t *)pkt;
scsi_qla_host_t *host = q2t_find_host_by_vp_idx(vha,
entry->vp_index);
if (unlikely(!host)) {
PRINT_ERROR("qla2x00t(%ld): Response pkt "
"(ABTS_RECV_24XX) received, with unknown "
@@ -741,6 +759,7 @@ static void q2t_response_pkt_all_vps(scsi_qla_host_t *vha, response_t *pkt)
abts24_resp_entry_t *entry = (abts24_resp_entry_t *)pkt;
scsi_qla_host_t *host = q2t_find_host_by_vp_idx(vha,
entry->vp_index);
if (unlikely(!host)) {
PRINT_ERROR("qla2x00t(%ld): Response pkt "
"(ABTS_RECV_24XX) received, with unknown "
@@ -882,6 +901,7 @@ static int q2t_reset(scsi_qla_host_t *vha, void *iocb, int mcmd)
if (IS_FWI2_CAPABLE(ha)) {
notify24xx_entry_t *n = (notify24xx_entry_t *)iocb;
if ((le16_to_cpu(n->status) == IMM_NTFY_ELS) &&
((n->status_subcode == ELS_TPRLO) ||
(n->status_subcode == ELS_LOGO))) {
@@ -1154,6 +1174,7 @@ retry:
res = -1;
for (i = 0; i < entries; i++) {
struct gid_list_info *gid = (struct gid_list_info *)id_iter;
if ((gid->al_pa == s_id[2]) &&
(gid->area == s_id[1]) &&
(gid->domain == s_id[0])) {
@@ -2053,6 +2074,7 @@ static int __q24_handle_abts(scsi_qla_host_t *vha, abts24_recv_entry_t *abts,
*/
while (!list_empty(&ha->unknown_atio_list)) {
struct q2t_unknown_atio *u;
u = list_first_entry(&ha->unknown_atio_list,
struct q2t_unknown_atio, unknown_atio_list_entry);
TRACE_MGMT_DBG("qla2x00t(%ld): Clearing unknown "
@@ -3304,6 +3326,7 @@ static int __q2t_rdy_to_xfer(struct q2t_cmd *cmd)
if (IS_FWI2_CAPABLE(ha)) {
ctio7_status0_entry_t *pkt;
res = q24_build_ctio_pkt(&prm);
if (unlikely(res != SCST_TGT_RES_SUCCESS))
goto out_unlock_free_unmap;
@@ -3314,6 +3337,7 @@ static int __q2t_rdy_to_xfer(struct q2t_cmd *cmd)
p = pkt;
} else {
ctio_common_entry_t *pkt;
q2x_build_ctio_pkt(&prm);
pkt = (ctio_common_entry_t *)prm.pkt;
pkt->flags = cpu_to_le16(OF_FAST_POST | OF_DATA_OUT);
@@ -3573,6 +3597,7 @@ static int q2t_prepare_srr_ctio(scsi_qla_host_t *vha, struct q2t_cmd *cmd,
sc, sc->srr_id);
if (tgt->imm_srr_id == tgt->ctio_srr_id) {
int found = 0;
list_for_each_entry(imm, &tgt->srr_imm_list,
srr_list_entry) {
if (imm->srr_id == sc->srr_id) {
@@ -3600,6 +3625,7 @@ static int q2t_prepare_srr_ctio(scsi_qla_host_t *vha, struct q2t_cmd *cmd,
spin_unlock(&tgt->srr_lock);
} else {
struct srr_imm *ti;
PRINT_ERROR("qla2x00t(%ld): Unable to allocate SRR CTIO entry",
vha->host_no);
spin_lock(&tgt->srr_lock);
@@ -3643,6 +3669,7 @@ static bool q2t_term_ctio_exchange(scsi_qla_host_t *vha, void *ctio,
}
if (ctio != NULL) {
ctio7_fw_entry_t *c = (ctio7_fw_entry_t *)ctio;
term = !(c->flags & cpu_to_le16(OF_TERM_EXCH));
} else
term = true;
@@ -3654,6 +3681,7 @@ static bool q2t_term_ctio_exchange(scsi_qla_host_t *vha, void *ctio,
#if 0 /* Seems, it isn't needed. If enable it, add support for NULL cmd! */
if (ctio != NULL) {
ctio_common_entry_t *c = (ctio_common_entry_t *)ctio;
term = !(c->flags & cpu_to_le16(CTIO7_FLAGS_TERMINATE));
} else
term = true;
@@ -3673,6 +3701,7 @@ static inline struct q2t_cmd *q2t_get_cmd(scsi_qla_host_t *vha, uint32_t handle)
handle--;
if (vha->cmds[handle] != NULL) {
struct q2t_cmd *cmd = vha->cmds[handle];
vha->cmds[handle] = NULL;
return cmd;
} else
@@ -3728,6 +3757,7 @@ static struct q2t_cmd *q2t_ctio_to_cmd(scsi_qla_host_t *vha, uint32_t handle,
goto out;
} else {
ctio_common_entry_t *c = (ctio_common_entry_t *)ctio;
loop_id = GET_TARGET_ID(ha, c);
tag = c->rx_id;
}
@@ -4103,6 +4133,7 @@ static int q2t_send_cmd_to_scst(scsi_qla_host_t *vha, atio_t *atio)
if (IS_FWI2_CAPABLE(ha)) {
atio7_entry_t *a = (atio7_entry_t *)atio;
sess = q2t_find_sess_by_s_id(tgt, a->fcp_hdr.s_id);
if (unlikely(sess == NULL)) {
TRACE_MGMT_DBG("qla2x00t(%ld): Unable to find "
@@ -4166,6 +4197,7 @@ static int q2t_issue_task_mgmt(struct q2t_sess *sess, uint8_t *lun,
*/
while (!list_empty(&ha->unknown_atio_list)) {
struct q2t_unknown_atio *u;
u = list_first_entry(&ha->unknown_atio_list,
struct q2t_unknown_atio, unknown_atio_list_entry);
TRACE_MGMT_DBG("qla2x00t(%ld): Clearing unknown "
@@ -4300,12 +4332,14 @@ static int q2t_handle_task_mgmt(scsi_qla_host_t *vha, void *iocb)
tgt = vha->tgt;
if (IS_FWI2_CAPABLE(ha)) {
atio7_entry_t *a = (atio7_entry_t *)iocb;
lun = (uint8_t *)&a->fcp_cmnd.lun;
lun_size = sizeof(a->fcp_cmnd.lun);
fn = a->fcp_cmnd.task_mgmt_flags;
sess = q2t_find_sess_by_s_id(tgt, a->fcp_hdr.s_id);
} else {
notify_entry_t *n = (notify_entry_t *)iocb;
/* make it be in network byte order */
lun_data = swab16(le16_to_cpu(n->lun));
lun = (uint8_t *)&lun_data;
@@ -4468,6 +4502,7 @@ static int q24_handle_els(scsi_qla_host_t *vha, notify24xx_entry_t *iocb)
case ELS_ADISC:
{
struct q2t_tgt *tgt = vha->tgt;
if (tgt->link_reinit_iocb_pending) {
q24_send_notify_ack(vha, &tgt->link_reinit_iocb, 0, 0, 0);
tgt->link_reinit_iocb_pending = 0;
@@ -4506,6 +4541,7 @@ static int q2t_cut_cmd_data_head(struct q2t_cmd *cmd, unsigned int offset)
l += cmd->sg[i].length;
if (l > offset) {
int sg_offs = l - cmd->sg[i].length;
first_sg = i;
if (cmd->sg[i].offset == 0) {
first_page_offs = offset % PAGE_SIZE;
@@ -4541,7 +4577,7 @@ static int q2t_cut_cmd_data_head(struct q2t_cmd *cmd, unsigned int offset)
first_sg, first_page, first_page_offs, cmd->bufflen,
cmd->sg_cnt);
sg = kmalloc(cnt * sizeof(sg[0]), GFP_KERNEL);
sg = kmalloc_array(cnt, sizeof(sg[0]), GFP_KERNEL);
if (sg == NULL) {
PRINT_ERROR("qla2x00t(%ld): Unable to allocate cut "
"SG (len %zd)", cmd->tgt->vha->host_no,
@@ -4555,6 +4591,7 @@ static int q2t_cut_cmd_data_head(struct q2t_cmd *cmd, unsigned int offset)
cur_src = first_sg;
if (first_page_offs != 0) {
int fpgs;
sg_set_page(&sg[cur_dst], &sg_page(&cmd->sg[cur_src])[first_page],
PAGE_SIZE - first_page_offs, first_page_offs);
bufflen += sg[cur_dst].length;
@@ -4657,6 +4694,7 @@ static void q24_handle_srr(scsi_qla_host_t *vha, struct srr_ctio *sctio,
(scst_cmd_get_data_direction(&cmd->scst_cmd) & SCST_DATA_READ)) {
uint32_t offset;
int xmit_type;
offset = le32_to_cpu(imm->imm.notify_entry24.srr_rel_offs);
if (q2t_srr_adjust_data(cmd, offset, &xmit_type) != 0)
goto out_reject;
@@ -4681,6 +4719,7 @@ static void q24_handle_srr(scsi_qla_host_t *vha, struct srr_ctio *sctio,
(scst_cmd_get_data_direction(&cmd->scst_cmd) & SCST_DATA_WRITE)) {
uint32_t offset;
int xmit_type;
offset = le32_to_cpu(imm->imm.notify_entry24.srr_rel_offs);
if (q2t_srr_adjust_data(cmd, offset, &xmit_type) != 0)
goto out_reject;
@@ -4751,6 +4790,7 @@ static void q2x_handle_srr(scsi_qla_host_t *vha, struct srr_ctio *sctio,
if (q2t_has_data(cmd)) {
uint32_t offset;
int xmit_type;
offset = le32_to_cpu(imm->imm.notify_entry.srr_rel_offs);
if (q2t_srr_adjust_data(cmd, offset, &xmit_type) != 0)
goto out_reject;
@@ -4773,6 +4813,7 @@ static void q2x_handle_srr(scsi_qla_host_t *vha, struct srr_ctio *sctio,
if (q2t_has_data(cmd)) {
uint32_t offset;
int xmit_type;
offset = le32_to_cpu(imm->imm.notify_entry.srr_rel_offs);
if (q2t_srr_adjust_data(cmd, offset, &xmit_type) != 0)
goto out_reject;
@@ -4949,6 +4990,7 @@ static void q2t_prepare_srr_imm(scsi_qla_host_t *vha, void *iocb)
imm->srr_id, iocb24->srr_ui);
if (tgt->imm_srr_id == tgt->ctio_srr_id) {
int found = 0;
list_for_each_entry(sctio, &tgt->srr_ctio_list,
srr_list_entry) {
if (sctio->srr_id == imm->srr_id) {
@@ -5064,6 +5106,7 @@ static void q2t_handle_imm_notify(scsi_qla_host_t *vha, void *iocb)
case IMM_NTFY_LIP_LINK_REINIT:
{
struct q2t_tgt *tgt = vha->tgt;
TRACE(TRACE_MGMT, "qla2x00t(%ld): LINK REINIT (loop %#x, "
"subcode %x)", vha->host_no,
le16_to_cpu(iocb24->nport_handle),
@@ -5359,6 +5402,7 @@ static void q24_atio_pkt(scsi_qla_host_t *vha, atio7_entry_t *atio)
case IMMED_NOTIFY_TYPE:
{
notify_entry_t *pkt = (notify_entry_t *)atio;
if (unlikely(pkt->entry_status != 0)) {
PRINT_ERROR("qla2x00t(%ld): Received ATIO packet %x "
"with error status %x", vha->host_no,
@@ -5426,6 +5470,7 @@ static void q2t_response_pkt(scsi_qla_host_t *vha, response_t *pkt)
case CTIO_TYPE7:
{
ctio7_fw_entry_t *entry = (ctio7_fw_entry_t *)pkt;
TRACE_DBG("CTIO_TYPE7: instance %ld",
vha->host_no);
TRACE_BUFFER("Incoming CTIO7 packet data", entry,
@@ -5440,6 +5485,7 @@ static void q2t_response_pkt(scsi_qla_host_t *vha, response_t *pkt)
{
atio_entry_t *atio;
int rc;
atio = (atio_entry_t *)pkt;
TRACE_DBG("ACCEPT_TGT_IO instance %ld status %04x "
"lun %04x read/write %d data_length %04x "
@@ -5481,6 +5527,7 @@ static void q2t_response_pkt(scsi_qla_host_t *vha, response_t *pkt)
case CONTINUE_TGT_IO_TYPE:
{
ctio_common_entry_t *entry = (ctio_common_entry_t *)pkt;
TRACE_DBG("CONTINUE_TGT_IO: instance %ld", vha->host_no);
TRACE_BUFFER("Incoming CTIO packet data", entry,
REQUEST_ENTRY_SIZE);
@@ -5493,6 +5540,7 @@ static void q2t_response_pkt(scsi_qla_host_t *vha, response_t *pkt)
case CTIO_A64_TYPE:
{
ctio_common_entry_t *entry = (ctio_common_entry_t *)pkt;
TRACE_DBG("CTIO_A64: instance %ld", vha->host_no);
TRACE_BUFFER("Incoming CTIO_A64 packet data", entry,
REQUEST_ENTRY_SIZE);
@@ -5510,6 +5558,7 @@ static void q2t_response_pkt(scsi_qla_host_t *vha, response_t *pkt)
case NOTIFY_ACK_TYPE:
if (tgt->notify_ack_expected > 0) {
nack_entry_t *entry = (nack_entry_t *)pkt;
TRACE_DBG("NOTIFY_ACK seq %08x status %x",
le16_to_cpu(entry->seq_id),
le16_to_cpu(entry->status));
@@ -5538,6 +5587,7 @@ static void q2t_response_pkt(scsi_qla_host_t *vha, response_t *pkt)
if (tgt->abts_resp_expected > 0) {
abts24_resp_fw_entry_t *entry =
(abts24_resp_fw_entry_t *)pkt;
TRACE_DBG("ABTS_RESP_24XX: compl_status %x",
entry->compl_status);
TRACE_BUFF_FLAG(TRACE_BUFF, "Incoming ABTS_RESP "
@@ -5574,6 +5624,7 @@ static void q2t_response_pkt(scsi_qla_host_t *vha, response_t *pkt)
case MODIFY_LUN_TYPE:
if (tgt->modify_lun_expected > 0) {
modify_lun_entry_t *entry = (modify_lun_entry_t *)pkt;
TRACE_DBG("MODIFY_LUN %x, imm %c%d, cmd %c%d",
entry->status,
(entry->operators & MODIFY_LUN_IMM_ADD) ? '+'
@@ -5599,6 +5650,7 @@ static void q2t_response_pkt(scsi_qla_host_t *vha, response_t *pkt)
case ENABLE_LUN_TYPE:
{
elun_entry_t *entry = (elun_entry_t *)pkt;
TRACE_DBG("ENABLE_LUN %x imm %u cmd %u ",
entry->status, entry->immed_notify_count,
entry->command_count);
@@ -5835,8 +5887,10 @@ static void q2t_exec_sess_work(struct q2t_tgt *tgt,
case Q2T_SESS_WORK_CMD:
{
struct q2t_cmd *cmd = prm->cmd;
if (IS_FWI2_CAPABLE(ha)) {
atio7_entry_t *a = (atio7_entry_t *)&cmd->atio;
s_id = a->fcp_hdr.s_id;
} else
loop_id = GET_TARGET_ID(ha, (atio_entry_t *)&cmd->atio);
@@ -5896,6 +5950,7 @@ send:
case Q2T_SESS_WORK_CMD:
{
struct q2t_cmd *cmd = prm->cmd;
if (tgt->tm_to_unknown) {
/*
* Cmd might be already aborted behind us, so be safe
@@ -5923,12 +5978,14 @@ send:
if (IS_FWI2_CAPABLE(ha)) {
atio7_entry_t *a = &prm->tm_iocb2;
iocb = a;
lun = (uint8_t *)&a->fcp_cmnd.lun;
lun_size = sizeof(a->fcp_cmnd.lun);
fn = a->fcp_cmnd.task_mgmt_flags;
} else {
notify_entry_t *n = &prm->tm_iocb;
iocb = n;
/* make it be in network byte order */
lun_data = swab16(le16_to_cpu(n->lun));
@@ -5962,6 +6019,7 @@ out_term:
case Q2T_SESS_WORK_CMD:
{
struct q2t_cmd *cmd = prm->cmd;
TRACE_MGMT_DBG("Terminating work cmd %p", cmd);
/*
* cmd has not sent to SCST yet, so pass NULL as the second
@@ -6195,8 +6253,10 @@ static int q2t_add_target(scsi_qla_host_t *vha)
scst_get_tgt_name(tgt->scst_tgt));
if (vha->vp_idx == 0) {
int i = 0;
while (1) {
const struct attribute *a = q2t_hw_tgt_attrs[i];
if (a == NULL)
break;
rc = sysfs_create_file(scst_sysfs_get_tgt_kobj(tgt->scst_tgt), a);
@@ -6209,8 +6269,10 @@ static int q2t_add_target(scsi_qla_host_t *vha)
}
} else {
int i = 0;
while (1) {
const struct attribute *a = q2t_npiv_tgt_attrs[i];
if (a == NULL)
break;
rc = sysfs_create_file(scst_sysfs_get_tgt_kobj(tgt->scst_tgt), a);
+1 -1
View File
@@ -31,7 +31,7 @@
/* Version numbers, the same as for the kernel */
#define Q2T_VERSION(a, b, c, d) (((a) << 030) + ((b) << 020) + (c) << 010 + (d))
#define Q2T_VERSION_CODE Q2T_VERSION(4, 1, 0, 0)
#define Q2T_VERSION_STRING "3.1.0-pre1"
#define Q2T_VERSION_STRING "3.2.0-pre1"
#define Q2T_PROC_VERSION_NAME "version"
#define Q2T_MAX_CDB_LEN 16
+2
View File
@@ -124,6 +124,7 @@ qla2x00_send_enable_lun(scsi_qla_host_t *vha, bool enable)
if (!IS_FWI2_CAPABLE(ha)) {
unsigned long flags;
spin_lock_irqsave(&ha->hardware_lock, flags);
__qla2x00_send_enable_lun(vha, enable);
spin_unlock_irqrestore(&ha->hardware_lock, flags);
@@ -159,6 +160,7 @@ static inline bool qla_firmware_active(scsi_qla_host_t *vha)
{
struct qla_hw_data *ha = vha->hw;
struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
return qla_tgt_mode_enabled(base_vha) || qla_ini_mode_enabled(base_vha);
}
+25 -25
View File
@@ -148,7 +148,7 @@ typedef struct {
uint16_t reserved_5;
uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */
uint16_t reserved_6[20];
} __attribute__((packed)) elun_entry_t;
} __packed elun_entry_t;
#define ENABLE_LUN_SUCCESS 0x01
#define ENABLE_LUN_RC_NONZERO 0x04
#define ENABLE_LUN_INVALID_REQUEST 0x06
@@ -179,7 +179,7 @@ typedef struct {
uint16_t reserved_5;
uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */
uint16_t reserved_7[20];
} __attribute__((packed)) modify_lun_entry_t;
} __packed modify_lun_entry_t;
#define MODIFY_LUN_SUCCESS 0x01
#define MODIFY_LUN_CMD_ADD BIT_0
#define MODIFY_LUN_CMD_SUB BIT_1
@@ -219,7 +219,7 @@ typedef struct {
uint16_t srr_ox_id;
uint8_t reserved_2[30];
uint16_t ox_id;
} __attribute__((packed)) notify_entry_t;
} __packed notify_entry_t;
#endif
#ifndef NOTIFY_ACK_TYPE
@@ -250,7 +250,7 @@ typedef struct {
uint8_t srr_reject_code_expl;
uint8_t reserved_2[26];
uint16_t ox_id;
} __attribute__((packed)) nack_entry_t;
} __packed nack_entry_t;
#define NOTIFY_ACK_SRR_FLAGS_ACCEPT 0
#define NOTIFY_ACK_SRR_FLAGS_REJECT 1
@@ -287,7 +287,7 @@ typedef struct {
uint8_t initiator_port_name[WWN_SIZE]; /* on qla23xx */
uint16_t reserved_32[6];
uint16_t ox_id;
} __attribute__((packed)) atio_entry_t;
} __packed atio_entry_t;
#endif
#ifndef CONTINUE_TGT_IO_TYPE
@@ -314,7 +314,7 @@ typedef struct {
uint16_t scsi_status;
uint32_t transfer_length;
uint32_t dseg_0_address[0];
} __attribute__((packed)) ctio_common_entry_t;
} __packed ctio_common_entry_t;
#define ATIO_PATH_INVALID 0x07
#define ATIO_CANT_PROV_CAP 0x16
#define ATIO_CDB_VALID 0x3D
@@ -333,7 +333,7 @@ typedef struct {
uint32_t dseg_1_length; /* Data segment 1 length. */
uint32_t dseg_2_address; /* Data segment 2 address. */
uint32_t dseg_2_length; /* Data segment 2 length. */
} __attribute__((packed)) ctio_entry_t;
} __packed ctio_entry_t;
#define CTIO_SUCCESS 0x01
#define CTIO_ABORTED 0x02
#define CTIO_INVALID_RX_ID 0x08
@@ -371,7 +371,7 @@ typedef struct {
uint16_t scsi_status;
uint16_t response_length;
uint8_t sense_data[26];
} __attribute__((packed)) ctio_ret_entry_t;
} __packed ctio_ret_entry_t;
#endif
#define ATIO_TYPE7 0x06 /* Accept target I/O entry for 24xx */
@@ -389,7 +389,7 @@ typedef struct {
uint16_t ox_id;
uint16_t rx_id;
uint32_t parameter;
} __attribute__((packed)) fcp_hdr_t;
} __packed fcp_hdr_t;
typedef struct {
uint8_t d_id[3];
@@ -404,7 +404,7 @@ typedef struct {
uint16_t rx_id;
uint16_t ox_id;
uint32_t parameter;
} __attribute__((packed)) fcp_hdr_le_t;
} __packed fcp_hdr_le_t;
#define F_CTL_EXCH_CONTEXT_RESP BIT_23
#define F_CTL_SEQ_CONTEXT_RESIP BIT_22
@@ -449,7 +449,7 @@ typedef struct {
*/
uint8_t add_cdb[4];
/* uint32_t data_length; */
} __attribute__((packed)) fcp_cmnd_t;
} __packed fcp_cmnd_t;
/*
* ISP queue - Accept Target I/O (ATIO) type 7 entry for 24xx structure
@@ -470,7 +470,7 @@ typedef struct {
#define ATIO_EXCHANGE_ADDRESS_UNKNOWN 0xFFFFFFFF
fcp_hdr_t fcp_hdr;
fcp_cmnd_t fcp_cmnd;
} __attribute__((packed)) atio7_entry_t;
} __packed atio7_entry_t;
#define CTIO_TYPE7 0x12 /* Continue target I/O entry (for 24xx) */
@@ -494,7 +494,7 @@ typedef struct {
uint8_t initiator_id[3];
uint8_t reserved;
uint32_t exchange_addr;
} __attribute__((packed)) ctio7_common_entry_t;
} __packed ctio7_common_entry_t;
typedef struct {
ctio7_common_entry_t common;
@@ -509,7 +509,7 @@ typedef struct {
uint32_t reserved3;
uint32_t dseg_0_address[2]; /* Data segment 0 address. */
uint32_t dseg_0_length; /* Data segment 0 length. */
} __attribute__((packed)) ctio7_status0_entry_t;
} __packed ctio7_status0_entry_t;
typedef struct {
ctio7_common_entry_t common;
@@ -521,7 +521,7 @@ typedef struct {
uint16_t response_len;
uint16_t reserved;
uint8_t sense_data[24];
} __attribute__((packed)) ctio7_status1_entry_t;
} __packed ctio7_status1_entry_t;
typedef struct {
uint8_t entry_type; /* Entry type. */
@@ -542,7 +542,7 @@ typedef struct {
uint16_t reserved3;
uint32_t relative_offset;
uint8_t reserved4[24];
} __attribute__((packed)) ctio7_fw_entry_t;
} __packed ctio7_fw_entry_t;
/* CTIO7 flags values */
#define CTIO7_FLAGS_SEND_STATUS BIT_15
@@ -589,7 +589,7 @@ typedef struct {
uint8_t reserved_7;
uint16_t reserved_8;
uint16_t ox_id;
} __attribute__((packed)) notify24xx_entry_t;
} __packed notify24xx_entry_t;
#define ELS_PLOGI 0x3
#define ELS_FLOGI 0x4
@@ -627,13 +627,13 @@ typedef struct {
uint8_t srr_reject_code;
uint8_t reserved_5[7];
uint16_t ox_id;
} __attribute__((packed)) nack24xx_entry_t;
} __packed nack24xx_entry_t;
/*
* ISP queue - ABTS received/response entries structure definition for 24xx.
*/
#define ABTS_RECV_24XX 0x54 /* ABTS received (for 24xx) */
#define ABTS_RESP_24XX 0x55 /* ABTS responce (for 24xx) */
#define ABTS_RESP_24XX 0x55 /* ABTS response (for 24xx) */
typedef struct {
uint8_t entry_type; /* Entry type. */
@@ -655,7 +655,7 @@ typedef struct {
fcp_hdr_le_t fcp_hdr_le;
uint8_t reserved_4[16];
uint32_t exchange_addr_to_abort;
} __attribute__((packed)) abts24_recv_entry_t;
} __packed abts24_recv_entry_t;
#define ABTS_PARAM_ABORT_SEQ BIT_0
@@ -669,7 +669,7 @@ typedef struct {
uint16_t ox_id;
uint16_t high_seq_cnt;
uint16_t low_seq_cnt;
} __attribute__((packed)) ba_acc_le_t;
} __packed ba_acc_le_t;
typedef struct {
uint8_t vendor_uniq;
@@ -678,7 +678,7 @@ typedef struct {
#define BA_RJT_REASON_CODE_INVALID_COMMAND 0x1
#define BA_RJT_REASON_CODE_UNABLE_TO_PERFORM 0x9
uint8_t reserved;
} __attribute__((packed)) ba_rjt_le_t;
} __packed ba_rjt_le_t;
typedef struct {
uint8_t entry_type; /* Entry type. */
@@ -703,10 +703,10 @@ typedef struct {
union {
ba_acc_le_t ba_acct;
ba_rjt_le_t ba_rjt;
} __attribute__((packed)) payload;
} __packed payload;
uint32_t reserved_4;
uint32_t exchange_addr_to_abort;
} __attribute__((packed)) abts24_resp_entry_t;
} __packed abts24_resp_entry_t;
typedef struct {
uint8_t entry_type; /* Entry type. */
@@ -734,7 +734,7 @@ typedef struct {
#define ABTS_RESP_SUBCODE_ERR_ABORTED_EXCH_NOT_TERM 0x1E
uint32_t error_subcode2;
uint32_t exchange_addr_to_abort;
} __attribute__((packed)) abts24_resp_fw_entry_t;
} __packed abts24_resp_fw_entry_t;
/********************************************************************\
* Type Definitions used by initiator & target halves
+4 -4
View File
@@ -126,12 +126,12 @@ struct qla84_msg_mgmt {
struct qla_bsg_a84_mgmt {
struct qla84_msg_mgmt mgmt;
} __attribute__ ((packed));
} __packed;
struct qla_scsi_addr {
uint16_t bus;
uint16_t target;
} __attribute__ ((packed));
} __packed;
struct qla_ext_dest_addr {
union {
@@ -144,13 +144,13 @@ struct qla_ext_dest_addr {
#define EXT_DEF_TYPE_WWPN 2
uint16_t lun;
uint16_t padding[2];
} __attribute__ ((packed));
} __packed;
struct qla_port_param {
struct qla_ext_dest_addr fc_scsi_addr;
uint16_t mode;
uint16_t speed;
} __attribute__ ((packed));
} __packed;
/* FRU VPD */
+3 -3
View File
@@ -361,7 +361,7 @@ struct device_reg_2xxx {
uint16_t mailbox6;
uint16_t mailbox7;
uint16_t unused_2[59]; /* Gap */
} __attribute__((packed)) isp2100;
} __packed isp2100;
struct {
/* Request Queue */
uint16_t req_q_in; /* In-Pointer */
@@ -412,7 +412,7 @@ struct device_reg_2xxx {
uint16_t mailbox31;
uint16_t fb_cmd;
uint16_t unused_4[10]; /* Gap */
} __attribute__((packed)) isp2300;
} __packed isp2300;
} u;
uint16_t fpm_diag_config;
@@ -469,7 +469,7 @@ struct device_reg_2xxx {
uint16_t mailbox21;
uint16_t mailbox22;
uint16_t mailbox23; /* Also probe reg. */
} __attribute__((packed)) isp2200;
} __packed isp2200;
} u_end;
};
+4 -4
View File
@@ -2632,8 +2632,8 @@ qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp)
struct qla_msix_entry *qentry;
scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
entries = kzalloc(sizeof(struct msix_entry) * ha->msix_count,
GFP_KERNEL);
entries = kcalloc(ha->msix_count, sizeof(struct msix_entry),
GFP_KERNEL);
if (!entries) {
ql_log(ql_log_warn, vha, 0x00bc,
"Failed to allocate memory for msix_entry.\n");
@@ -2664,8 +2664,8 @@ msix_failed:
}
ha->max_rsp_queues = ha->msix_count - 1;
}
ha->msix_entries = kzalloc(sizeof(struct qla_msix_entry) *
ha->msix_count, GFP_KERNEL);
ha->msix_entries = kcalloc(ha->msix_count,
sizeof(struct qla_msix_entry), GFP_KERNEL);
if (!ha->msix_entries) {
ql_log(ql_log_fatal, vha, 0x00c8,
"Failed to allocate memory for ha->msix_entries.\n");
+1 -1
View File
@@ -1222,7 +1222,7 @@ qla82xx_pinit_from_rom(scsi_qla_host_t *vha)
ql_log(ql_log_info, vha, 0x0072,
"%d CRB init values found in ROM.\n", n);
buf = kmalloc(n * sizeof(struct crb_addr_pair), GFP_KERNEL);
buf = kmalloc_array(n, sizeof(struct crb_addr_pair), GFP_KERNEL);
if (buf == NULL) {
ql_log(ql_log_fatal, vha, 0x010c,
"Unable to allocate memory.\n");
+8 -6
View File
@@ -367,7 +367,7 @@ static int qla2x00_alloc_queues(struct qla_hw_data *ha, struct req_que *req,
struct rsp_que *rsp)
{
scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
ha->req_q_map = kzalloc(sizeof(struct req_que *) * ha->max_req_queues,
ha->req_q_map = kcalloc(ha->max_req_queues, sizeof(struct req_que *),
GFP_KERNEL);
if (!ha->req_q_map) {
ql_log(ql_log_fatal, vha, 0x003b,
@@ -375,7 +375,7 @@ static int qla2x00_alloc_queues(struct qla_hw_data *ha, struct req_que *req,
goto fail_req_map;
}
ha->rsp_q_map = kzalloc(sizeof(struct rsp_que *) * ha->max_rsp_queues,
ha->rsp_q_map = kcalloc(ha->max_rsp_queues, sizeof(struct rsp_que *),
GFP_KERNEL);
if (!ha->rsp_q_map) {
ql_log(ql_log_fatal, vha, 0x003c,
@@ -3286,8 +3286,9 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
#ifdef CONFIG_SCSI_QLA2XXX_TARGET
if (IS_FWI2_CAPABLE(ha)) {
ha->tgt_vp_map = kzalloc(sizeof(struct qla_tgt_vp_map) *
MAX_MULTI_ID_FABRIC, GFP_KERNEL);
ha->tgt_vp_map = kcalloc(MAX_MULTI_ID_FABRIC,
sizeof(struct qla_tgt_vp_map),
GFP_KERNEL);
if (!ha->tgt_vp_map)
goto fail_free_init_cb;
@@ -3433,8 +3434,9 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
(*rsp)->ring);
/* Allocate memory for NVRAM data for vports */
if (ha->nvram_npiv_size) {
ha->npiv_info = kzalloc(sizeof(struct qla_npiv_entry) *
ha->nvram_npiv_size, GFP_KERNEL);
ha->npiv_info = kcalloc(ha->nvram_npiv_size,
sizeof(struct qla_npiv_entry),
GFP_KERNEL);
if (!ha->npiv_info) {
ql_log_pci(ql_log_fatal, ha->pdev, 0x002d,
"Failed to allocate memory for npiv_info.\n");
+2 -1
View File
@@ -302,7 +302,8 @@ do
done
scst_03_public_headers="scst/include/scst.h scst/include/scst_const.h scst/include/backport.h"
scst_03_public_headers="scst/include/scst.h scst/include/scst_const.h \
scst/include/scst_event.h scst/include/backport.h"
scst_04_main="scst/src/scst_main.c scst/src/scst_module.c scst/src/scst_priv.h"
scst_05_targ="scst/src/scst_targ.c"
scst_06_lib="scst/src/scst_lib.c"
+2 -1
View File
@@ -8,7 +8,8 @@
# version number when building on a koji build server.
# - Otherwise use the version number of the running kernel.
%{!?kversion:
%{?kdir:%define kversion %{expand:%%(make -sC %{kdir} kernelversion)}}
%{?kdir:%define kversion %{expand:%%(
make -sC "%{kdir}" kernelversion | grep -v ^make)}}
%{!?kdir:
%define kversion %{expand:%%(
if rpm --quiet -q kernel-headers; then
+233 -30
View File
@@ -1,7 +1,7 @@
Generic SCSI target mid-level for Linux (SCST)
==============================================
Version 3.1.0, XX XXXXX 2014
Version 3.2.0, XX XXXXX 2015
----------------------------
SCST is designed to provide unified, consistent interface between SCSI
@@ -508,6 +508,8 @@ documentation for your dev handlers for more info about it as well as
SysfsRules file for more info about common to all dev handlers rules.
SCST dev handlers can have the following common entries:
- block - allows to temporary block and unblock this device. See below.
- exported - subdirectory containing links to all LUNs where this
device was exported.
@@ -531,6 +533,35 @@ SCST dev handlers can have the following common entries:
- type - SCSI type of this device
Attribute "block" allows to temporary block and unblock this device.
"Blocking" means that no new commands for this device will go into the
execution stage, but instead will be suspended just before it. The
blocked state is not reached until queue of the corresponding device is
completely drained. You can also call this state "frozen". It is useful
in many cases, like consistent snapshots and graceful shutdown.
On write "block" entry allows the following 3 types of parameters:
- 1 - block device synchronously, i.e. don't return until this device
becomes blocked, i.e. until queue of it is not completely drained. Can
be called as many times as needed.
- 11 params - block device asynchronously, i.e. return immediately.
Notification about completing is delivered using SCST_EVENT_EXT_BLOCKING_DONE
event. "Params" delivered to it as is in "data" payload. Can be
called as many times as needed. Alternatively, status of blocking could be
polled by reading this attributes until the second number reaches 0
(see below).
- 0 - unblock this device.
Reading from "block" entry returns two numbers separated by space:
1. How many times this device was blocked, i.e. how many times writing
"0" to it is needed to unblock this device.
2. Boolean (0 or 1) if blocking, if any, is done (0) or still pending (1).
See below for more information about other entries of this subdirectory
of the standard SCST dev handlers.
@@ -1257,6 +1288,7 @@ Each vdisk_fileio's device has the following attributes in
For example:
/sys/kernel/scst_tgt/devices/disk1
|-- block
|-- blocksize
|-- exported
| |-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/0
@@ -1420,16 +1452,18 @@ persistent reservations from this device are released, upon reconnect
the initiators will see it.
Implicit ALUA Support
---------------------
ALUA Support
------------
SCST supports implicit asymmetric logical unit access (ALUA). Implicit ALUA is
a feature defined by the ANSI T10 SCSI committee that allows a target to tell
the initiator which path to use in a multipath setup. The redundant paths
between initiator and target can be used either for redundancy or for load
sharing purposes. The target can either be a single target system running SCST
with multiple communication interfaces or two target systems each running SCST
and configured in a high availability setup.
SCST supports both implicit and explicit asymmetric logical unit access
(ALUA). ALUA is a feature defined by the ANSI T10 SCSI committee. It
allows a target to tell the initiator which path to use in a multipath
setup plus, in the explicit case, control state of each path via SET
TARGET PORT GROUPS SCSI command. The redundant paths between initiator
and target can be used either for redundancy or for load sharing
purposes. The target can either be a single target system running SCST
with multiple communication interfaces or two target systems each
running SCST and configured in a high availability setup.
In the SPC-4 standard the following concepts are defined related to ALUA:
* Relative target port ID. A number between 1 and 65535 that uniquely
@@ -1450,17 +1484,17 @@ ANSI T10 standard called SPC-4.
ALUA support in SCST
....................
SCST allows to define implicit ALUA settings for each unique combination of
SCST device and SCST target. An initiator however queries ALUA settings by
sending an appropriate SCSI command to a specific LUN of an SCST target. Each
such LUN maps uniquely to an SCST device. For hardware SCST target drivers,
e.g. ib_srpt, there is a one-to-one correspondence between SCST target and
SCSI target port. With other SCST targets, e.g. iSCSI-SCST, by default the
only relationship between SCST targets and SCSI target ports is that all SCST
targets defined on a system are visible via all SCSI target ports. See also
the iSCSI-SCST documentation about the allowed_portal attribute for
information about how to associate iSCSI targets with a single physical
interface.
SCST allows to define ALUA settings for each unique combination of SCST
device and SCST target. An initiator however queries ALUA settings by
sending an appropriate SCSI command to a specific LUN of an SCST target.
Each such LUN maps uniquely to an SCST device. For hardware SCST target
drivers, e.g. ib_srpt, there is a one-to-one correspondence between SCST
target and SCSI target port. With other SCST targets, e.g. iSCSI-SCST,
by default the only relationship between SCST targets and SCSI target
ports is that all SCST targets defined on a system are visible via all
SCSI target ports. See also the iSCSI-SCST documentation about the
allowed_portal attribute for information about how to associate iSCSI
targets with a single physical interface.
Notes:
- In a H.A. setup it is the responsibility of the user to synchronize ALUA
@@ -1483,7 +1517,7 @@ Notes:
Configuring ALUA in SCST
........................
SCST allows to configure the following settings related to implicit ALUA
SCST allows to configure the following settings related to ALUA
for each unique combination of SCST target and virtual SCST device
(vdisk_fileio, vdisk_blockio, vcdrom, ...):
* The target port group asymmetric access state. SCST supports all ALUA port
@@ -1581,15 +1615,62 @@ DEVICE_GROUP dgroup2 {
}
}
Explicit ALUA
.............
To enable explicit ALUA you need in addition to the above settings set
expl_alua device attribute to 1 (by default it is 0). Also you need to
run stpgd and supply to it path to a script or program, which will
perform actual path state switching on SET TARGET PORT GROUPS command,
for instance, by calling drbdadm. For more information see stpgd README
as well as sample script scst_on_stpg.
DRBD compatibility
..................
DRBD does not allow to open its device on the secondary as well as does
not allow to perform primary to secondary transition, if this device is
open.
SCST BLOCKIO handler has all the necessary support for this behavior. If
you write new ALUA state in the "state" attribute, SCST BLOCKIO handler
before transition closes the open devices' handles and after transition
reopens them, if the new state is active or nonoptimized.
Thus, the recommended implicit ALUA state change procedure for primary
to secondary transition is:
1. Block all involved SCST devices using "block" sysfs attribute (see
above). Wait until the blocking finished.
2. Change the ALUA state to "transitioning". At this moment all open
file handles will be closed.
3. Perform the DRBD state transition
4. Change the ALUA state to your desired secondary state.
5. Unblock the blocked on step 1 devices.
Optionally, if your initiators support Transitioning ALUA state, for
more responsive behavior the blocked devices can be unblocked
immediately after step (2). However, not all initiators correctly
behave, if they receive ASYMMETRIC STATE TRANSITION sense.
For the secondary to primary transition procedure is similar.
In case of explicit ALUA, SCST automatically performs the necessary
devices blocking around sending SCST_EVENT_STPG_USER_INVOKE event.
Checking the Target Configuration
.................................
One way to verify the implicit ALUA configuration from a Linux initiator is
via the commands provided in the sg3_utils package. The first step is to
verify whether for a certain LUN implicit ALUA has been configured on the
target. This is possible by checking whether the TPGS=1 text appears in the
sg_inq output, where /dev/sdb is a device node created by the ib_srp initiator:
One way to verify the ALUA configuration from a Linux initiator is via
the commands provided in the sg3_utils package. The first step is to
verify whether for a certain LUN ALUA has been configured on the target.
This is possible by checking whether the TPGS=1 text appears in the
sg_inq output, where /dev/sdb is a device node created by the ib_srp
initiator:
# sg_inq /dev/sdb
standard INQUIRY:
@@ -1649,9 +1730,11 @@ Device Identification VPD page:
Initiator Support
.................
On Linux systems implicit ALUA support is provided by the scsi_dh_alua kernel
driver in combination with the user space multipathd daemon. You will have to
modify at least the following in /etc/multipath.conf to enable implicit ALUA:
On Linux systems ALUA support is provided by the scsi_dh_alua kernel
driver in combination with the user space multipathd daemon. You will
have to modify at least the following in /etc/multipath.conf to enable
ALUA:
* hardware_handler "1 alua"
* prio alua
* path_grouping_policy group_by_prio
@@ -1700,6 +1783,126 @@ information about ALUA support in Windows Server, see also:
(http://msdn.microsoft.com/en-us/library/gg607458%28v=vs.85%29.aspx).
VAAI
----
SCST supports all 3 VAAI SCSI commands: WRITE SAME, COMPARE AND WRITE
(ATS) and EXTENDED COPY. Additionally, it supports not directly related
to VAAI Thin Provisioning capabilities, particularly, UNMAP SCSI
commands, WRITE SAME with UNMAP bit as well as thin provisioning related
devices' sysfs attributes (see above).
In some cases dev handlers should perform some manual actions to fully
benefit from SCST VAAI implementation. Those actions described in the
implementation notes below. For vdisk and fileio_tgt handlers they have
already been implemented.
Implementation notes
....................
WRITE SAME
~~~~~~~~~~
WRITE SAME command supports 2 modes:
1. Manual writing mode. In this mode WRITE SAME generates a set of
internal WRITE(16) SCSI commands to perform requested writing.
2. Remap mode. In this mode a dev handler, if supported, can remap being
written blocks to a single block and then tell SCST to manually write
parts of the requested area, which for some reason can not be remapped.
In both cases dev handlers should call from WRITE SAME command handler
scst_write_same() function. This function as the second argument gets
array of descriptors where to write the requested block of data. Last
element in this array must have len 0. If this argument is NULL, then
the whole area will be manually written by SCST. This value should be
used by dev handlers not supporting remapping blocks.
User space dev handlers should use SCST_EXEC_REPLY_DO_WRITE_SAME
reply_type of SCST_USER_EXEC subcommand. See scst_user doc for more
info.
COMPARE AND WRITE
~~~~~~~~~~~~~~~~~
COMPARE AND WRITE implemented by SCST a set of read, compare and write
actions done in atomic manner against affected blocks as well as regular
RESERVE SCSI commands. Particularly, COMPARE AND WRITE doesn't need any
queue flushing and unlimited number of COMPARE AND WRITE commands on
different blocks can be executed simultaneously.
The read and write actions implemented as generation of internal
READ(16) and WRITE(16) SCSI commands.
COMPARE AND WRITE command is completely transparent to dev handlers
(they only see the corresponding READ(16) and WRITE(16) commands), so
doesn't require any manual actions from them.
EXTENDED COPY
~~~~~~~~~~~~~
SCST implements EXTENDED COPY via internal Copy Manager target. This
target has the following specific attributes in its sysfs:
- allow_not_connected_copy - if not set (default), an initiator can
perform copy only between devices it has direct access to via any
target/session. If set, any initiator can copy between any devices in
the system.
- mgmt - this attribute allows to control data from which devices can
be copied using Copy Manager. By default, devices belonging to dev
handlers with flag auto_cm_assignment_possible set are auto assigned to
the Copy Manager on the registration. Currently, only vdisk has this
flag set, so all other devices (pass-through, user space, etc.) should
be assigned to the Copy Manager manually.
Mgmt attribute supports the following commands:
- add [vname|H:C:I:L] - adds device with name vname or H:C:I:L numbers
(pass-through) to the Copy Manager.
- del [vname|H:C:I:L] - deletes device with name vname or H:C:I:L numbers
(pass-through) from the Copy Manager.
Internally SCST implements EXTENDED COPY as generation of sets of
internal READ(16) and WRITE(16) SCSI commands. Dev handlers don't need
any manual actions to use it.
Also SCST provides for dev handlers possibility to remap blocks instead
of copy them, if they support this feature. It allows them to perform
EXTENDED COPY command much faster by just metadata update of their
backend storage, which supposed to be nearly instantaneous.
To use this feature, a dev handler should setup ext_copy_remap()
callback in its struct scst_dev_type. This callback is called by SCST
during EXTENDED COPY command processing to let the dev handler try to
remap affected blocks at first.
Upon finish, the dev handler should call scst_ext_copy_remap_done(). In
case of error, the dev handler should set the corresponding sense to cmd
and then also call scst_ext_copy_remap_done(cmd, NULL, 0).
If dev handler is not able to remap any part of the segment, if should
kmalloc(), then fill all leftover subsegments and supply them to
scst_ext_copy_remap_done(). SCST then will copy the subsegments using
internal copy machine, then kfree() the supplied array. If the dev
handler is not able to remap the whole segment, it can simply directly
supply the original segment to scst_ext_copy_remap_done().
It is highly recommended that in normal circumstances dev handlers call
scst_ext_copy_remap_done() from another thread context than one where
ext_copy_remap() callback was originally called, because otherwise there
could be recursion in the segments processing. Hopefully, this thread
context switch is natural for such potentially long operation as
EXTENDED COPY.
Caching
-------
-27
View File
@@ -143,33 +143,6 @@ change the t10_dev_id if cluster mode has been enabled.
Testing
-------
A quick test to verify whether PR synchronization is working is as follows:
* Start SCST and enable cluster_mode for a volume on both nodes.
* Log in from an initiator system to the two target systems.
* Look up the names of the paths on the initiator system with lsscsi. Assume
that these names are /dev/sdb and /dev/sdc.
* Run the following commands on the initiator system:
# sg_persist -s /dev/sdc
# sg_persist -n -o --register --param-sark 0xabc123 /dev/sdb
# sg_persist -s /dev/sdc
SCST_BIO ram0 310
Peripheral device type: disk
PR generation=0x1
Key=0xabc123
All target ports bit clear
Relative port address: 0x1
not reservation holder
Transport Id of initiator:
iSCSI name and session id: iqn...
* Run the following command on a target system:
# dlm_tool ls
dlm lockspaces
name scst-179ee149-ram0
id 0x2b2d1c39
flags 0x00000000
change member 2 joined 1 remove 0 failed 0 seq 1,1
members 1 2
Two examples of test suites for the cluster PR support code are:
* The SCSI conformance tests in the libiscsi project.
* The Windows Cluster Validation Tests
+232 -29
View File
@@ -367,6 +367,8 @@ documentation for your dev handlers for more info about it as well as
SysfsRules file for more info about common to all dev handlers rules.
SCST dev handlers can have the following common entries:
- block - allows to temporary block and unblock this device. See below.
- exported - subdirectory containing links to all LUNs where this
device was exported.
@@ -390,6 +392,35 @@ SCST dev handlers can have the following common entries:
- type - SCSI type of this device
Attribute "block" allows to temporary block and unblock this device.
"Blocking" means that no new commands for this device will go into the
execution stage, but instead will be suspended just before it. The
blocked state is not reached until queue of the corresponding device is
completely drained. You can also call this state "frozen". It is useful
in many cases, like consistent snapshots and graceful shutdown.
On write "block" entry allows the following 3 types of parameters:
- 1 - block device synchronously, i.e. don't return until this device
becomes blocked, i.e. until queue of it is not completely drained. Can
be called as many times as needed.
- 11 params - block device asynchronously, i.e. return immediately.
Notification about completing is delivered using SCST_EVENT_EXT_BLOCKING_DONE
event. "Params" delivered to it as is in "data" payload. Can be
called as many times as needed. Alternatively, status of blocking could be
polled by reading this attributes until the second number reaches 0
(see below).
- 0 - unblock this device.
Reading from "block" entry returns two numbers separated by space:
1. How many times this device was blocked, i.e. how many times writing
"0" to it is needed to unblock this device.
2. Boolean (0 or 1) if blocking, if any, is done (0) or still pending (1).
See below for more information about other entries of this subdirectory
of the standard SCST dev handlers.
@@ -1110,6 +1141,7 @@ Each vdisk_fileio's device has the following attributes in
For example:
/sys/kernel/scst_tgt/devices/disk1
|-- block
|-- blocksize
|-- exported
| |-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/0
@@ -1268,16 +1300,18 @@ persistent reservations from this device are released, upon reconnect
the initiators will see it.
Implicit ALUA Support
---------------------
ALUA Support
------------
SCST supports implicit asymmetric logical unit access (ALUA). Implicit ALUA is
a feature defined by the ANSI T10 SCSI committee that allows a target to tell
the initiator which path to use in a multipath setup. The redundant paths
between initiator and target can be used either for redundancy or for load
sharing purposes. The target can either be a single target system running SCST
with multiple communication interfaces or two target systems each running SCST
and configured in a high availability setup.
SCST supports both implicit and explicit asymmetric logical unit access
(ALUA). ALUA is a feature defined by the ANSI T10 SCSI committee. It
allows a target to tell the initiator which path to use in a multipath
setup plus, in the explicit case, control state of each path via SET
TARGET PORT GROUPS SCSI command. The redundant paths between initiator
and target can be used either for redundancy or for load sharing
purposes. The target can either be a single target system running SCST
with multiple communication interfaces or two target systems each
running SCST and configured in a high availability setup.
In the SPC-4 standard the following concepts are defined related to ALUA:
* Relative target port ID. A number between 1 and 65535 that uniquely
@@ -1298,17 +1332,17 @@ ANSI T10 standard called SPC-4.
ALUA support in SCST
....................
SCST allows to define implicit ALUA settings for each unique combination of
SCST device and SCST target. An initiator however queries ALUA settings by
sending an appropriate SCSI command to a specific LUN of an SCST target. Each
such LUN maps uniquely to an SCST device. For hardware SCST target drivers,
e.g. ib_srpt, there is a one-to-one correspondence between SCST target and
SCSI target port. With other SCST targets, e.g. iSCSI-SCST, by default the
only relationship between SCST targets and SCSI target ports is that all SCST
targets defined on a system are visible via all SCSI target ports. See also
the iSCSI-SCST documentation about the allowed_portal attribute for
information about how to associate iSCSI targets with a single physical
interface.
SCST allows to define ALUA settings for each unique combination of SCST
device and SCST target. An initiator however queries ALUA settings by
sending an appropriate SCSI command to a specific LUN of an SCST target.
Each such LUN maps uniquely to an SCST device. For hardware SCST target
drivers, e.g. ib_srpt, there is a one-to-one correspondence between SCST
target and SCSI target port. With other SCST targets, e.g. iSCSI-SCST,
by default the only relationship between SCST targets and SCSI target
ports is that all SCST targets defined on a system are visible via all
SCSI target ports. See also the iSCSI-SCST documentation about the
allowed_portal attribute for information about how to associate iSCSI
targets with a single physical interface.
Notes:
- In a H.A. setup it is the responsibility of the user to synchronize ALUA
@@ -1331,7 +1365,7 @@ Notes:
Configuring ALUA in SCST
........................
SCST allows to configure the following settings related to implicit ALUA
SCST allows to configure the following settings related to ALUA
for each unique combination of SCST target and virtual SCST device
(vdisk_fileio, vdisk_blockio, vcdrom, ...):
* The target port group asymmetric access state. SCST supports all ALUA port
@@ -1429,15 +1463,62 @@ DEVICE_GROUP dgroup2 {
}
}
Explicit ALUA
.............
To enable explicit ALUA you need in addition to the above settings set
expl_alua device attribute to 1 (by default it is 0). Also you need to
run stpgd and supply to it path to a script or program, which will
perform actual path state switching on SET TARGET PORT GROUPS command,
for instance, by calling drbdadm. For more information see stpgd README
as well as sample script scst_on_stpg.
DRBD compatibility
..................
DRBD does not allow to open its device on the secondary as well as does
not allow to perform primary to secondary transition, if this device is
open.
SCST BLOCKIO handler has all the necessary support for this behavior. If
you write new ALUA state in the "state" attribute, SCST BLOCKIO handler
before transition closes the open devices' handles and after transition
reopens them, if the new state is active or nonoptimized.
Thus, the recommended implicit ALUA state change procedure for primary
to secondary transition is:
1. Block all involved SCST devices using "block" sysfs attribute (see
above). Wait until the blocking finished.
2. Change the ALUA state to "transitioning". At this moment all open
file handles will be closed.
3. Perform the DRBD state transition
4. Change the ALUA state to your desired secondary state.
5. Unblock the blocked on step 1 devices.
Optionally, if your initiators support Transitioning ALUA state, for
more responsive behavior the blocked devices can be unblocked
immediately after step (2). However, not all initiators correctly
behave, if they receive ASYMMETRIC STATE TRANSITION sense.
For the secondary to primary transition procedure is similar.
In case of explicit ALUA, SCST automatically performs the necessary
devices blocking around sending SCST_EVENT_STPG_USER_INVOKE event.
Checking the Target Configuration
.................................
One way to verify the implicit ALUA configuration from a Linux initiator is
via the commands provided in the sg3_utils package. The first step is to
verify whether for a certain LUN implicit ALUA has been configured on the
target. This is possible by checking whether the TPGS=1 text appears in the
sg_inq output, where /dev/sdb is a device node created by the ib_srp initiator:
One way to verify the ALUA configuration from a Linux initiator is via
the commands provided in the sg3_utils package. The first step is to
verify whether for a certain LUN ALUA has been configured on the target.
This is possible by checking whether the TPGS=1 text appears in the
sg_inq output, where /dev/sdb is a device node created by the ib_srp
initiator:
# sg_inq /dev/sdb
standard INQUIRY:
@@ -1497,9 +1578,11 @@ Device Identification VPD page:
Initiator Support
.................
On Linux systems implicit ALUA support is provided by the scsi_dh_alua kernel
driver in combination with the user space multipathd daemon. You will have to
modify at least the following in /etc/multipath.conf to enable implicit ALUA:
On Linux systems ALUA support is provided by the scsi_dh_alua kernel
driver in combination with the user space multipathd daemon. You will
have to modify at least the following in /etc/multipath.conf to enable
ALUA:
* hardware_handler "1 alua"
* prio alua
* path_grouping_policy group_by_prio
@@ -1548,6 +1631,126 @@ information about ALUA support in Windows Server, see also:
(http://msdn.microsoft.com/en-us/library/gg607458%28v=vs.85%29.aspx).
VAAI
----
SCST supports all 3 VAAI SCSI commands: WRITE SAME, COMPARE AND WRITE
(ATS) and EXTENDED COPY. Additionally, it supports not directly related
to VAAI Thin Provisioning capabilities, particularly, UNMAP SCSI
commands, WRITE SAME with UNMAP bit as well as thin provisioning related
devices' sysfs attributes (see above).
In some cases dev handlers should perform some manual actions to fully
benefit from SCST VAAI implementation. Those actions described in the
implementation notes below. For vdisk and fileio_tgt handlers they have
already been implemented.
Implementation notes
....................
WRITE SAME
~~~~~~~~~~
WRITE SAME command supports 2 modes:
1. Manual writing mode. In this mode WRITE SAME generates a set of
internal WRITE(16) SCSI commands to perform requested writing.
2. Remap mode. In this mode a dev handler, if supported, can remap being
written blocks to a single block and then tell SCST to manually write
parts of the requested area, which for some reason can not be remapped.
In both cases dev handlers should call from WRITE SAME command handler
scst_write_same() function. This function as the second argument gets
array of descriptors where to write the requested block of data. Last
element in this array must have len 0. If this argument is NULL, then
the whole area will be manually written by SCST. This value should be
used by dev handlers not supporting remapping blocks.
User space dev handlers should use SCST_EXEC_REPLY_DO_WRITE_SAME
reply_type of SCST_USER_EXEC subcommand. See scst_user doc for more
info.
COMPARE AND WRITE
~~~~~~~~~~~~~~~~~
COMPARE AND WRITE implemented by SCST a set of read, compare and write
actions done in atomic manner against affected blocks as well as regular
RESERVE SCSI commands. Particularly, COMPARE AND WRITE doesn't need any
queue flushing and unlimited number of COMPARE AND WRITE commands on
different blocks can be executed simultaneously.
The read and write actions implemented as generation of internal
READ(16) and WRITE(16) SCSI commands.
COMPARE AND WRITE command is completely transparent to dev handlers
(they only see the corresponding READ(16) and WRITE(16) commands), so
doesn't require any manual actions from them.
EXTENDED COPY
~~~~~~~~~~~~~
SCST implements EXTENDED COPY via internal Copy Manager target. This
target has the following specific attributes in its sysfs:
- allow_not_connected_copy - if not set (default), an initiator can
perform copy only between devices it has direct access to via any
target/session. If set, any initiator can copy between any devices in
the system.
- mgmt - this attribute allows to control data from which devices can
be copied using Copy Manager. By default, devices belonging to dev
handlers with flag auto_cm_assignment_possible set are auto assigned to
the Copy Manager on the registration. Currently, only vdisk has this
flag set, so all other devices (pass-through, user space, etc.) should
be assigned to the Copy Manager manually.
Mgmt attribute supports the following commands:
- add [vname|H:C:I:L] - adds device with name vname or H:C:I:L numbers
(pass-through) to the Copy Manager.
- del [vname|H:C:I:L] - deletes device with name vname or H:C:I:L numbers
(pass-through) from the Copy Manager.
Internally SCST implements EXTENDED COPY as generation of sets of
internal READ(16) and WRITE(16) SCSI commands. Dev handlers don't need
any manual actions to use it.
Also SCST provides for dev handlers possibility to remap blocks instead
of copy them, if they support this feature. It allows them to perform
EXTENDED COPY command much faster by just metadata update of their
backend storage, which supposed to be nearly instantaneous.
To use this feature, a dev handler should setup ext_copy_remap()
callback in its struct scst_dev_type. This callback is called by SCST
during EXTENDED COPY command processing to let the dev handler try to
remap affected blocks at first.
Upon finish, the dev handler should call scst_ext_copy_remap_done(). In
case of error, the dev handler should set the corresponding sense to cmd
and then also call scst_ext_copy_remap_done(cmd, NULL, 0).
If dev handler is not able to remap any part of the segment, if should
kmalloc(), then fill all leftover subsegments and supply them to
scst_ext_copy_remap_done(). SCST then will copy the subsegments using
internal copy machine, then kfree() the supplied array. If the dev
handler is not able to remap the whole segment, it can simply directly
supply the original segment to scst_ext_copy_remap_done().
It is highly recommended that in normal circumstances dev handlers call
scst_ext_copy_remap_done() from another thread context than one where
ext_copy_remap() callback was originally called, because otherwise there
could be recursion in the segments processing. Hopefully, this thread
context switch is natural for such potentially long operation as
EXTENDED COPY.
Caching
-------
+35
View File
@@ -255,6 +255,32 @@ static inline void hex2bin(u8 *dst, const char *src, size_t count)
}
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)
static inline int __must_check kstrtoull(const char *s, unsigned int base,
unsigned long long *res)
{
return strict_strtoull(s, base, res);
}
static inline int __must_check kstrtoll(const char *s, unsigned int base,
long long *res)
{
return strict_strtoll(s, base, res);
}
static inline int __must_check kstrtoul(const char *s, unsigned int base,
unsigned long *res)
{
return strict_strtoul(s, base, res);
}
static inline int __must_check kstrtol(const char *s, unsigned int base,
long *res)
{
return strict_strtol(s, base, res);
}
#endif
/* <linux/list.h> */
#ifndef __list_for_each
@@ -401,6 +427,15 @@ static inline void sg_set_page(struct scatterlist *sg, struct page *page,
(__flags), NULL, NULL)
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
static inline void *kmalloc_array(size_t n, size_t size, gfp_t flags)
{
if (size != 0 && n > ULONG_MAX / size)
return NULL;
return kmalloc(n * size, flags);
}
#endif
/* <linux/t10-pi.h> */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0)
+211 -10
View File
@@ -684,6 +684,7 @@ struct scst_acg;
struct scst_acg_dev;
struct scst_acn;
struct scst_aen;
struct scst_ext_copy_seg_descr;
struct scst_opcode_descriptor;
/*
@@ -1330,6 +1331,12 @@ struct scst_dev_type {
*/
unsigned pr_cmds_notifications:1;
/*
* Set if the Copy Manager can auto assing to devices of this
* template on their registration.
*/
unsigned auto_cm_assignment_possible:1;
/*
* Called to parse CDB from the cmd and initialize
* cmd->bufflen and cmd->data_direction (both - REQUIRED).
@@ -1420,6 +1427,50 @@ struct scst_dev_type {
*/
void (*on_free_cmd)(struct scst_cmd *cmd);
/*
* Called during EXTENDED COPY command processing to let dev handler
* try to remap blocks at first. Upon finish, the dev handler supposed
* to call scst_ext_copy_remap_done(). See description of this
* function for more details.
*
* In case of error, the dev handler should set the corresponding sense
* to cmd and then call scst_ext_copy_remap_done(cmd, NULL, 0).
*
* It is highly recommended that in the normal circumstances
* scst_ext_copy_remap_done() called from another thread context,
* because otherwise there will be recursion in the segments processing.
* Hopefully, this thread context switch is natural for such
* potentially long operation.
*
* OPTIONAL
*/
void (*ext_copy_remap)(struct scst_cmd *cmd,
struct scst_ext_copy_seg_descr *descr);
/*
* Called to notify dev handler that a ALUA state change is about to
* be started. Can be used to close open file handlers, which might
* prevent the state switch.
*
* Called under scst_dg_mutex and no activities on the dev handler level.
*
* OPTIONAL
*/
void (*on_alua_state_change_start)(struct scst_device *dev,
enum scst_tg_state old_state, enum scst_tg_state new_state);
/*
* Called to notify dev handler that a ALUA state change is about to
* be finished. Can be used to (re)open file handlers closed in
* on_alua_state_change_start().
*
* Called under scst_dg_mutex and no activities on the dev handler level.
*
* OPTIONAL
*/
void (*on_alua_state_change_finish)(struct scst_device *dev,
enum scst_tg_state old_state, enum scst_tg_state new_state);
/*
* Called to notify dev handler that a task management command received
*
@@ -1918,6 +1969,14 @@ struct scst_session {
/* Used if scst_unregister_session() called in wait mode */
struct completion *shutdown_compl;
/*
* Keep list IDs for Extended Copy commands sent over this session.
* Protected by scst_cm_lock.
*/
struct list_head sess_cm_list_id_list;
struct delayed_work sess_cm_list_id_cleanup_work;
/* sysfs release completion */
struct completion *sess_kobj_release_cmpl;
@@ -2110,12 +2169,24 @@ struct scst_cmd {
/* Set if cmd is internally generated */
unsigned int internal:1;
/* Set if local events should be checked for internally generated cmd */
unsigned int internal_check_local_events:1;
/* Set if the blocking machinery should be bypassed for this cmd */
unsigned int bypass_blocking:1;
/* Set if the device was blocked by scst_check_blocked_dev() */
unsigned int unblock_dev:1;
/* Set if scst_dec_on_dev_cmd() call is needed on the cmd's finish */
unsigned int dec_on_dev_needed:1;
/* Set if cmd is on dev's exec_cmd_list */
unsigned int on_dev_exec_list:1;
/* Set if this cmd passed check for SCSI atomicity */
unsigned int scsi_atomicity_checked:1;
/* Set if cmd is queued as hw pending */
unsigned int cmd_hw_pending:1;
@@ -2208,12 +2279,21 @@ struct scst_cmd {
/* Set if cmd was pre-alloced by target driver */
unsigned int pre_alloced:1;
/* Set if cmd was already ALUA checked in TRANSITIONING state */
unsigned int already_transitioning:1;
/* Set if scst_cmd_set_write_not_received_data_len() was called */
unsigned int write_not_received_set:1;
/* Set if cmd has LINK bit set in CDB */
unsigned int cmd_linked:1;
/* Set if cmd is on scst_global_stpg_list */
unsigned int cmd_on_global_stpg_list:1;
/* Set if cmd was globally STPG blocked in __scst_check_blocked_dev() */
unsigned int cmd_global_stpg_blocked:1;
/**************************************************************/
/* cmd's async flags */
@@ -2269,6 +2349,15 @@ struct scst_cmd {
unsigned short cdb_len;
uint8_t cdb_buf[SCST_MAX_CDB_SIZE];
/* List entry for dev's dev_exec_cmd_list */
struct list_head dev_exec_cmd_list_entry;
/*
* Array of blocked by this cmd SCSI atomic cmds with size
* scsi_atomic_blocked_cmds_count. Protected by dev->dev_lock.
*/
struct scst_cmd **scsi_atomic_blocked_cmds;
uint8_t lba_off; /* LBA offset in cdb */
uint8_t lba_len; /* LBA length in cdb */
uint8_t len_off; /* length offset in cdb */
@@ -2400,6 +2489,18 @@ struct scst_cmd {
/* Used for storage of dev handler private stuff */
void *dh_priv;
/*
* Number of waiting for this cmd to finish commands
* with SCSI atomic guarantees. Protected by dev->dev_lock.
*/
int scsi_atomic_blockers;
/*
* How many SCSI atomic cmds this cmd blocked, i.e. size of array
* scsi_atomic_blocked_cmds. Protected by dev->dev_lock.
*/
int scsi_atomic_blocked_cmds_count;
/* List entry for dev's blocked_cmd_list */
struct list_head blocked_cmd_list_entry;
@@ -2429,6 +2530,11 @@ struct scst_cmd {
void *cmd_data_descriptors;
int cmd_data_descriptors_cnt;
};
/* STPG commands global serialization */
struct {
struct list_head global_stpg_list_entry;
};
};
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
@@ -2440,6 +2546,7 @@ struct scst_cmd {
uint64_t restart_waiting_time, rdy_to_xfer_time;
uint64_t pre_exec_time, exec_time, dev_done_time;
uint64_t xmit_time;
bool exec_time_counting;
#endif
#ifdef CONFIG_SCST_DEBUG_TM
@@ -2611,6 +2718,20 @@ struct scst_cl_ops {
void (*reserve)(struct scst_device *dev, struct scst_session *sess);
};
/*
* Extended, e.g. via sysfs, blockers
*/
typedef void (*ext_blocker_done_fn_t) (struct scst_device *dev,
uint8_t *data, int len);
struct scst_ext_blocker {
struct list_head ext_blockers_list_entry;
ext_blocker_done_fn_t ext_blocker_done_fn;
int ext_blocker_data_len;
uint8_t ext_blocker_data[];
};
/*
* SCST device
*/
@@ -2631,6 +2752,26 @@ struct scst_device {
/* Set, if a strictly serialized cmd is waiting blocked */
unsigned int strictly_serialized_cmd_waiting:1;
/*
* Set, if this device is being unregistered. Useful to let sysfs
* attributes know when they should exit immediately to prevent
* possible deadlocks with their device unregistration waiting for
* their kobj last put.
*/
unsigned int dev_unregistering:1;
/*
* Set if ext blocking is pending. It if just shortcut for
* !list_empty(&dev->ext_blockers_list) to save a cache miss.
*/
unsigned int ext_blocking_pending:1;
/* Used to serialize invocations of __scst_ext_blocking_done() */
unsigned int ext_unblock_scheduled:1;
/* Set if this device was blocked during STPG command processing */
unsigned int stpg_ext_blocked:1;
/* Set if this device does not support DIF IP checking */
unsigned int dev_dif_ip_not_supported:1;
@@ -2744,15 +2885,23 @@ struct scst_device {
*/
int on_dev_cmd_count;
/*
* How many atomic SCSI commands being executed. Protected by dev_lock.
*/
int dev_scsi_atomic_cmd_active;
/*
* List of all being executed on the dev commands.
* Protected by dev_lock.
*/
struct list_head dev_exec_cmd_list;
/* Memory limits for this device */
struct scst_mem_lim dev_mem_lim;
/* List of commands with lock, if dedicated threads are used */
struct scst_cmd_threads dev_cmd_threads;
/* Operations that depend on whether or not cluster mode is enabled */
const struct scst_cl_ops *cl_ops;
/*************************************************************
** T10-PI fields. Read-only, hence no protection.
*************************************************************/
@@ -2770,9 +2919,11 @@ struct scst_device {
int (*dev_dif_fn)(struct scst_cmd *cmd);
__be16 dev_dif_static_app_tag; /* fixed APP TAG for all blocks in dev */
__be32 dev_dif_static_app_ref_tag; /* fixed APP TAG part from REF
* TAG for all blocks in dev.
* Valid only with dif type 3 */
/*
* Fixed APP TAG part from REF TAG for all blocks in dev. Valid only
* with dif type 3.
*/
__be32 dev_dif_static_app_ref_tag;
/* Cache to optimize scst_parse_*protect() routines */
enum scst_dif_actions dev_dif_rd_actions;
@@ -2784,6 +2935,9 @@ struct scst_device {
/* Set if reserved via the SPC-2 SCSI RESERVE command. */
struct scst_session *reserved_by;
/* Operations that depend on whether or not cluster mode is enabled */
const struct scst_cl_ops *cl_ops;
/**********************************************************************
* Persistent reservation fields. Protected as follows:
* - Reading PR data must be protected via scst_pr_read_lock() /
@@ -2854,6 +3008,15 @@ struct scst_device {
/* List of blocked commands, protected by dev_lock. */
struct list_head blocked_cmd_list;
/* Number of ext blocking requests, protected by dev_lock */
int ext_blocks_cnt;
/* List of ext blockers, protected by dev_lock */
struct list_head ext_blockers_list;
/* Work to notify ext blockers out of dev_lock context */
struct work_struct ext_blockers_work;
/* MAXIMUM WRITE SAME LENGTH in bytes */
uint64_t max_write_same_len;
@@ -2969,7 +3132,10 @@ struct scst_tgt_dev {
atomic_t tgt_dev_cmd_count ____cacheline_aligned_in_smp;
/* ALUA command filter */
bool (*alua_filter)(struct scst_cmd *cmd);
#define SCST_ALUA_CHECK_OK 0
#define SCST_ALUA_CHECK_DELAYED 1
#define SCST_ALUA_CHECK_ERROR -1
int (*alua_filter)(struct scst_cmd *cmd);
struct scst_order_data *curr_order_data;
struct scst_order_data tgt_dev_order_data;
@@ -3176,6 +3342,8 @@ struct scst_acn {
* @kobj: For making this object visible in sysfs.
* @dev_kobj: Sysfs devices directory.
* @tg_kobj: Sysfs target groups directory.
* @stpg_transport_id Initiator transport ID for STPG originating I_T nexus, if any
* @stpg_rel_tgt_id Relative target ID for STPG originating I_T nexus, if any
*
* Each device is member of zero or one device groups. With each device group
* there are zero or more target groups associated.
@@ -3188,6 +3356,8 @@ struct scst_dev_group {
struct kobject kobj;
struct kobject *dev_kobj;
struct kobject *tg_kobj;
uint8_t *stpg_transport_id;
uint16_t stpg_rel_tgt_id;
};
/**
@@ -3300,6 +3470,7 @@ extern const struct scst_opcode_descriptor scst_op_descr_stpg;
extern const struct scst_opcode_descriptor scst_op_descr_send_diagnostic;
extern const struct scst_opcode_descriptor scst_op_descr_inquiry;
extern const struct scst_opcode_descriptor scst_op_descr_extended_copy;
extern const struct scst_opcode_descriptor scst_op_descr_tur;
extern const struct scst_opcode_descriptor scst_op_descr_reserve6;
extern const struct scst_opcode_descriptor scst_op_descr_release6;
@@ -3326,7 +3497,6 @@ extern const struct scst_opcode_descriptor scst_op_descr_report_supp_opcodes;
&scst_op_descr_report_supp_opcodes, \
&scst_op_descr_report_supp_tm_fns,
#ifndef smp_mb__after_set_bit
/* There is no smp_mb__after_set_bit() in the kernel */
#define smp_mb__after_set_bit() smp_mb()
@@ -3655,9 +3825,12 @@ static inline void scst_sess_set_tgt_priv(struct scst_session *sess,
}
uint16_t scst_lookup_tg_id(struct scst_device *dev, struct scst_tgt *tgt);
bool scst_impl_alua_configured(struct scst_device *dev);
bool scst_alua_configured(struct scst_device *dev);
int scst_tg_get_group_info(void **buf, uint32_t *response_length,
struct scst_device *dev, uint8_t data_format);
int scst_tg_set_group_info(struct scst_cmd *cmd);
const char *scst_alua_state_name(enum scst_tg_state s);
void scst_stpg_del_unblock_next(struct scst_cmd *cmd);
/*
* Get/set functions for dev's static DIF APP TAG
@@ -5453,7 +5626,7 @@ struct scst_data_descriptor {
uint64_t sdd_blocks;
};
void scst_write_same(struct scst_cmd *cmd);
void scst_write_same(struct scst_cmd *cmd, struct scst_data_descriptor *where);
__be64 scst_pack_lun(const uint64_t lun, enum scst_lun_addr_method addr_method);
uint64_t scst_unpack_lun(const uint8_t *lun, int len);
@@ -5478,4 +5651,32 @@ int scst_pr_set_cluster_mode(struct scst_device *dev, bool cluster_mode,
int scst_pr_init_dev(struct scst_device *dev);
void scst_pr_clear_dev(struct scst_device *dev);
struct scst_ext_copy_data_descr {
uint64_t src_lba;
uint64_t dst_lba;
int data_len; /* in bytes */
};
struct scst_ext_copy_seg_descr {
#define SCST_EXT_COPY_SEG_DATA 0
#if 0 /* not implemented yet */
#define SCST_EXT_COPY_SEG_PR_REG 1
#define SCST_EXT_COPY_SEG_PR_MOVE 2
#endif
int type;
union {
struct {
struct scst_tgt_dev *src_tgt_dev;
struct scst_tgt_dev *dst_tgt_dev;
struct scst_ext_copy_data_descr data_descr;
};
};
/* Internal, don't touch! */
int tgt_descr_offs;
};
void scst_ext_copy_remap_done(struct scst_cmd *ec_cmd,
struct scst_ext_copy_data_descr *dds, int dds_cnt);
int scst_ext_copy_get_cur_seg_data_len(struct scst_cmd *ec_cmd);
#endif /* __SCST_H */
+31 -7
View File
@@ -50,7 +50,7 @@
#else
#define SCST_VERSION_STRING_SUFFIX
#endif
#define SCST_VERSION_NAME "3.1.0-pre1"
#define SCST_VERSION_NAME "3.2.0-pre1"
#define SCST_VERSION_STRING SCST_VERSION_NAME SCST_VERSION_STRING_SUFFIX
#define SCST_CONST_VERSION SCST_CONST_INTF_VER
@@ -228,9 +228,11 @@ enum scst_cdb_flags {
#ifdef CONFIG_SCST_TEST_IO_IN_SIRQ
SCST_TEST_IO_IN_SIRQ_ALLOWED = 0x8000,
#endif
SCST_SERIALIZED = 0x10000,
SCST_STRICTLY_SERIALIZED = 0x20000|SCST_SERIALIZED,
SCST_DESCRIPTORS_BASED = 0x40000,
SCST_SERIALIZED = 0x10000,
SCST_STRICTLY_SERIALIZED = 0x20000|SCST_SERIALIZED,
SCST_CAN_GEN_3PARTY_COMMANDS = 0x40000,
SCST_DESCRIPTORS_BASED = 0x80000,
SCST_SCSI_ATOMIC = 0x100000,
};
/*************************************************************
@@ -290,9 +292,9 @@ static inline int scst_sense_response_code(const uint8_t *sense)
/* NOT_READY is 2 */
#define scst_sense_format_in_progress NOT_READY, 0x04, 0x04
#define scst_sense_tp_transitioning NOT_READY, 0x04, 0x0A
#define scst_sense_tp_standby NOT_READY, 0x04, 0x0B
#define scst_sense_tp_unav NOT_READY, 0x04, 0x0C
#define scst_sense_alua_transitioning NOT_READY, 0x04, 0x0A
#define scst_sense_alua_standby NOT_READY, 0x04, 0x0B
#define scst_sense_alua_unav NOT_READY, 0x04, 0x0C
#define scst_sense_no_medium NOT_READY, 0x3a, 0
/* MEDIUM_ERROR is 3 */
@@ -301,8 +303,11 @@ static inline int scst_sense_response_code(const uint8_t *sense)
/* HARDWARE_ERROR is 4 */
#define scst_sense_hardw_error HARDWARE_ERROR, 0x44, 0 /* non-retriable */
#define scst_sense_set_target_pgs_failed HARDWARE_ERROR, 0x67, 0xA
/* ILLEGAL_REQUEST is 5 */
#define scst_sense_operation_in_progress ILLEGAL_REQUEST, 0x0, 0x16
#define scst_sense_parameter_list_length_invalid ILLEGAL_REQUEST, 0x1A, 0
#define scst_sense_invalid_opcode ILLEGAL_REQUEST, 0x20, 0
#define scst_sense_block_out_range_error ILLEGAL_REQUEST, 0x21, 0
/* Don't use it directly, use scst_set_invalid_field_in_cdb() instead! */
@@ -312,6 +317,11 @@ static inline int scst_sense_response_code(const uint8_t *sense)
#define scst_sense_invalid_field_in_parm_list ILLEGAL_REQUEST, 0x26, 0
#define scst_sense_parameter_value_invalid ILLEGAL_REQUEST, 0x26, 2
#define scst_sense_invalid_release ILLEGAL_REQUEST, 0x26, 4
#define scst_sense_too_many_target_descriptors ILLEGAL_REQUEST, 0x26, 6
#define scst_sense_unsupported_tgt_descr_type ILLEGAL_REQUEST, 0x26, 7
#define scst_sense_too_many_segment_descriptors ILLEGAL_REQUEST, 0x26, 8
#define scst_sense_unsupported_seg_descr_type ILLEGAL_REQUEST, 0x26, 9
#define scst_sense_inline_data_length_exceeded ILLEGAL_REQUEST, 0x26, 0xB
#define scst_sense_saving_params_unsup ILLEGAL_REQUEST, 0x39, 0
#define scst_sense_invalid_message ILLEGAL_REQUEST, 0x49, 0
#define scst_sense_parameter_list_length_invalid ILLEGAL_REQUEST, 0x1A, 0
@@ -419,6 +429,14 @@ static inline int scst_sense_response_code(const uint8_t *sense)
#define WRITE_SAME_16 0x93
#endif
#ifndef EXTENDED_COPY
#define EXTENDED_COPY 0x83
#endif
#ifndef RECEIVE_COPY_RESULTS
#define RECEIVE_COPY_RESULTS 0x84
#endif
#ifndef SYNCHRONIZE_CACHE_16
#define SYNCHRONIZE_CACHE_16 0x91
#endif
@@ -588,6 +606,12 @@ enum {
#define SCSI_TRANSPORTID_PROTOCOLID_ISCSI 5
#define SCSI_TRANSPORTID_PROTOCOLID_SAS 6
/*
* SCST extension. Added, because there is no TransportID for Copy Manager
* defined anywhere in the SCSI standards.
*/
#define SCST_TRANSPORTID_PROTOCOLID_COPY_MGR 0xC
/**
* enum scst_tg_state - SCSI target port group asymmetric access state.
*
+157
View File
@@ -0,0 +1,157 @@
/*
* include/scst_event.h
*
* Copyright (C) 2014 - 2015 SanDisk Corporation
*
* Contains constants and data structures for scst_event module.
*/
#ifndef __SCST_EVENT_H
#define __SCST_EVENT_H
#ifdef __KERNEL__
#include <linux/types.h>
#endif
#ifdef INSIDE_KERNEL_TREE
#include <scst/scst_const.h>
#else
#include "scst_const.h"
#endif
#define SCST_EVENT_NAME "scst_event"
#define SCST_EVENT_PATH "/dev/"
#define SCST_EVENT_DEV SCST_EVENT_PATH SCST_EVENT_NAME
#define SCST_EVENT_VERSION_NAME SCST_VERSION_NAME
#define SCST_EVENT_VERSION \
SCST_EVENT_VERSION_NAME "$Revision: 2454 $" SCST_CONST_VERSION
#ifndef aligned_u64
#define aligned_u64 uint64_t __attribute__((aligned(8)))
#endif
/*
* Due to variable size, this structure must always be last in any outer
* structure!
*/
struct scst_event {
int32_t payload_len;
uint32_t event_code;
uint32_t event_id; /* ID uniquely identifying this event */
/*
* Event's issuer's name, i.e. SCST name of the corresponding module
* (target driver or dev handler). SCST_EVENT_SCST_CORE_ISSUER for SCST
* core.
*/
char issuer_name[SCST_MAX_NAME];
uint8_t payload[]; /* event's payload */
};
#ifdef __KERNEL__
typedef void (*scst_event_done_notify_fn) (struct scst_event *event,
void *priv, int status);
struct scst_event_entry {
struct list_head events_list_entry;
scst_event_done_notify_fn event_notify_fn;
void *notify_fn_priv;
unsigned long event_timeout; /* in jiffies */
int *pqueued_events_cnt;
union {
struct work_struct scst_event_queue_work;
struct delayed_work event_timeout_work;
};
struct scst_event event;
/* event's payload */
};
#endif
struct scst_event_user {
int32_t max_event_size;
struct scst_event out_event;
};
struct scst_event_notify_done {
uint32_t event_id;
int status;
};
/* IOCTLs */
#define SCST_EVENT_ALLOW_EVENT _IOW('u', 1, struct scst_event)
#define SCST_EVENT_DISALLOW_EVENT _IOW('u', 2, struct scst_event)
#define SCST_EVENT_GET_NEXT_EVENT _IOWR('u', 3, struct scst_event_user)
#define SCST_EVENT_NOTIFY_DONE _IOW('u', 4, struct scst_event_notify_done)
#ifdef __KERNEL__
void scst_event_queue(uint32_t event_code, const char *issuer_name,
struct scst_event_entry *e);
#endif
/*************************************************************
** SCST events
*************************************************************/
/* SCST core issuer name */
#define SCST_EVENT_SCST_CORE_ISSUER "SCST core"
/** SCST core's events **/
#define SCST_EVENT_LUN_NOT_FOUND 1
struct scst_event_lun_not_found_payload {
aligned_u64 lun;
uint8_t initiator_name[SCST_MAX_EXTERNAL_NAME];
uint8_t target_name[SCST_MAX_EXTERNAL_NAME];
};
#define SCST_EVENT_NEGATIVE_LUNS_INQUIRY 2
struct scst_event_negative_luns_inquiry_payload {
uint8_t initiator_name[SCST_MAX_EXTERNAL_NAME];
uint8_t target_name[SCST_MAX_EXTERNAL_NAME];
};
#define SCST_EVENT_EXT_BLOCKING_DONE 3
struct scst_event_ext_blocking_done_payload {
uint8_t device_name[SCST_MAX_EXTERNAL_NAME];
uint32_t data_len;
uint8_t data[];
};
#define SCST_EVENT_TM_FN_RECEIVED 4
struct scst_event_tm_fn_received_payload {
uint32_t fn; /* TM fn */
aligned_u64 lun;
uint8_t device_name[SCST_MAX_EXTERNAL_NAME];
uint8_t initiator_name[SCST_MAX_EXTERNAL_NAME];
uint8_t target_name[SCST_MAX_EXTERNAL_NAME];
uint8_t session_sysfs_name[SCST_MAX_EXTERNAL_NAME];
struct { /* if ABORT TASK, then tag and CDB of cmd to abort */
aligned_u64 cmd_to_abort_tag;
uint8_t cdb[SCST_MAX_CDB_SIZE];
};
};
#define SCST_EVENT_STPG_USER_INVOKE 5
struct scst_event_stpg_descr {
uint16_t group_id;
/*
* Better to keep below fields as small as possible to fit
* in single page as many descriptors as possible.
*/
uint8_t prev_state[32];
uint8_t new_state[32];
uint8_t dg_name[64];
uint8_t tg_name[64];
};
struct scst_event_stpg_payload {
aligned_u64 stpg_cmd_tag;
uint8_t device_name[64];
uint16_t stpg_descriptors_cnt;
struct scst_event_stpg_descr stpg_descriptors[0];
};
#endif /* __SCST_EVENT_H */
+51 -4
View File
@@ -71,10 +71,11 @@
#define UCMD_STATE_BUF_ALLOCING 2
#define UCMD_STATE_EXECING 3
#define UCMD_STATE_ON_FREEING 4
#define UCMD_STATE_ON_FREE_SKIPPED 5
#define UCMD_STATE_ON_CACHE_FREEING 6
#define UCMD_STATE_TM_RECEIVED_EXECING 7
#define UCMD_STATE_TM_DONE_EXECING 8
#define UCMD_STATE_ON_CACHE_FREEING 5
#define UCMD_STATE_EXT_COPY_REMAPPING 6
#define UCMD_STATE_ON_FREE_SKIPPED 7
#define UCMD_STATE_TM_RECEIVED_EXECING 8
#define UCMD_STATE_TM_DONE_EXECING 9
#define UCMD_STATE_ATTACH_SESS 0x20
#define UCMD_STATE_DETACH_SESS 0x21
@@ -96,6 +97,8 @@ struct scst_user_opt {
uint8_t d_sense;
uint8_t has_own_order_mgmt;
uint8_t ext_copy_remap_supported;
};
struct scst_user_dev_desc {
@@ -212,6 +215,19 @@ struct scst_user_tm {
uint8_t cmd_sn_set;
};
struct scst_user_ext_copy_data_descr {
aligned_u64 src_lba;
aligned_u64 dst_lba;
int32_t data_len; /* in bytes */
};
struct scst_user_ext_copy_remap {
aligned_u64 sess_h;
aligned_u64 src_sess_h;
aligned_u64 dst_sess_h;
struct scst_user_ext_copy_data_descr data_descr;
};
struct scst_user_get_cmd {
uint32_t cmd_h;
uint32_t subcode;
@@ -224,6 +240,7 @@ struct scst_user_get_cmd {
struct scst_user_scsi_on_free_cmd on_free_cmd;
struct scst_user_on_cached_mem_free on_cached_mem_free;
struct scst_user_tm tm_cmd;
struct scst_user_ext_copy_remap remap_cmd;
};
};
@@ -253,6 +270,15 @@ struct scst_user_scsi_cmd_reply_alloc_mem {
aligned_u64 pbuf;
};
/*
* Same as struct scst_data_descriptor, but suitable to pass kernel/user
* space boundary
*/
struct scst_user_data_descriptor {
aligned_u64 usdd_lba;
aligned_u64 usdd_blocks;
};
/* Be careful adding new members here, this structure is allocated on stack! */
struct scst_user_scsi_cmd_reply_exec {
int32_t resp_data_len;
@@ -260,8 +286,26 @@ struct scst_user_scsi_cmd_reply_exec {
#define SCST_EXEC_REPLY_BACKGROUND 0
#define SCST_EXEC_REPLY_COMPLETED 1
#define SCST_EXEC_REPLY_DO_WRITE_SAME 2
uint8_t reply_type;
uint8_t status;
union {
struct {
uint8_t sense_len;
aligned_u64 psense_buffer;
};
struct {
uint16_t ws_descriptors_len;
aligned_u64 ws_descriptors;
};
};
};
/* Be careful adding new members here, this structure is allocated on stack! */
struct scst_user_ext_copy_reply_remap {
aligned_u64 remap_descriptors;
uint16_t remap_descriptors_len;
uint8_t status;
uint8_t sense_len;
aligned_u64 psense_buffer;
@@ -276,6 +320,7 @@ struct scst_user_reply_cmd {
struct scst_user_scsi_cmd_reply_parse parse_reply;
struct scst_user_scsi_cmd_reply_alloc_mem alloc_reply;
struct scst_user_scsi_cmd_reply_exec exec_reply;
struct scst_user_ext_copy_reply_remap remap_reply;
};
};
@@ -344,5 +389,7 @@ struct scst_user_get_multi {
_IOWR('s', UCMD_STATE_TM_RECEIVED_EXECING, struct scst_user_tm)
#define SCST_USER_TASK_MGMT_DONE \
_IOWR('s', UCMD_STATE_TM_DONE_EXECING, struct scst_user_tm)
#define SCST_USER_EXT_COPY_REMAP \
_IOWR('s', UCMD_STATE_EXT_COPY_REMAPPING, struct scst_user_ext_copy_remap)
#endif /* __SCST_USER_H */
@@ -0,0 +1,13 @@
diff --git a/drivers/Kconfig b/drivers/Kconfig
index c70d6e4..0a4ed1b 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -26,6 +26,8 @@ source "drivers/ide/Kconfig"
source "drivers/scsi/Kconfig"
+source "drivers/scst/Kconfig"
+
source "drivers/ata/Kconfig"
source "drivers/md/Kconfig"
@@ -0,0 +1,14 @@
ccflags-y += -Wno-unused-parameter
obj-m := scst_cdrom.o scst_changer.o scst_disk.o scst_modisk.o scst_tape.o \
scst_vdisk.o scst_raid.o scst_processor.o scst_user.o
obj-$(CONFIG_SCST_DISK) += scst_disk.o
obj-$(CONFIG_SCST_TAPE) += scst_tape.o
obj-$(CONFIG_SCST_CDROM) += scst_cdrom.o
obj-$(CONFIG_SCST_MODISK) += scst_modisk.o
obj-$(CONFIG_SCST_CHANGER) += scst_changer.o
obj-$(CONFIG_SCST_RAID) += scst_raid.o
obj-$(CONFIG_SCST_PROCESSOR) += scst_processor.o
obj-$(CONFIG_SCST_VDISK) += scst_vdisk.o
obj-$(CONFIG_SCST_USER) += scst_user.o
@@ -0,0 +1,12 @@
diff --git a/drivers/Makefile b/drivers/Makefile
index 527a6da..db2c24f 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -137,6 +137,7 @@ obj-$(CONFIG_SSB) += ssb/
obj-$(CONFIG_BCMA) += bcma/
obj-$(CONFIG_VHOST_RING) += vhost/
obj-$(CONFIG_VLYNQ) += vlynq/
+obj-$(CONFIG_SCST) += scst/
obj-$(CONFIG_STAGING) += staging/
obj-y += platform/
#common clk code
+13
View File
@@ -0,0 +1,13 @@
ccflags-y += -Wno-unused-parameter
scst-y += scst_main.o
scst-y += scst_pres.o
scst-y += scst_targ.o
scst-y += scst_lib.o
scst-y += scst_sysfs.o
scst-y += scst_mem.o
scst-y += scst_tg.o
scst-y += scst_debug.o
obj-$(CONFIG_SCST) += scst.o dev_handlers/ fcst/ iscsi-scst/ qla2xxx-target/ \
srpt/ scst_local/
+2
View File
@@ -56,6 +56,8 @@ ifdef CONFIG_DLM_MODULE
scst-y += scst_dlm.o
endif
scst-y += scst_tg.o
scst-y += scst_event.o
scst-y += scst_copy_mgr.o
obj-$(CONFIG_SCST) += scst.o dev_handlers/
obj-$(BUILD_DEV) += $(DEV_HANDLERS_DIR)/
+2
View File
@@ -92,6 +92,8 @@ EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECKS
#EXTRA_CFLAGS += -DCONFIG_SCST_TRACING
EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions
#EXTRA_CFLAGS += -DCONFIG_DEBUG_EXT_COPY_REMAP
clean:
rm -f *.o *.ko .*.cmd *.mod.c .*.d .depend Modules.symvers \
Module.symvers Module.markers modules.order
+1
View File
@@ -123,6 +123,7 @@ static int cdrom_attach(struct scst_device *dev)
if (rc == 0) {
uint32_t sector_size = get_unaligned_be32(&buffer[4]);
if (sector_size == 0)
dev->block_shift = CDROM_DEF_BLOCK_SHIFT;
else
+2
View File
@@ -217,6 +217,7 @@ static int disk_attach(struct scst_device *dev)
}
if (rc == 0) {
uint32_t sector_size = get_unaligned_be32(&buffer[4]);
if (sector_size == 0)
dev->block_shift = DISK_DEF_BLOCK_SHIFT;
else
@@ -476,6 +477,7 @@ split:
if (unlikely(sg_is_chain(&sg[j]))) {
bool reset_start_sg = (start_sg == &sg[j]);
sg = sg_chain_ptr(&sg[j]);
j = 0;
if (reset_start_sg)
+1
View File
@@ -222,6 +222,7 @@ static int modisk_attach(struct scst_device *dev)
if (rc == 0) {
uint32_t sector_size = get_unaligned_be32(&buffer[4]);
if (sector_size == 0)
dev->block_shift = MODISK_DEF_BLOCK_SHIFT;
else
+3
View File
@@ -206,6 +206,7 @@ static int tape_attach(struct scst_device *dev)
if (rc == 0) {
int medium_type, mode, speed, density;
if (buffer[3] == 8)
dev->block_size = get_unaligned_be24(&buffer[9]);
else
@@ -299,6 +300,7 @@ static int tape_done(struct scst_cmd *cmd)
(cmd->sense[2] & 0xe0)) {
/* EOF, EOM, or ILI */
unsigned int TransferLength, Residue = 0;
if ((cmd->sense[2] & 0x0f) == BLANK_CHECK)
/* No need for EOM in this case */
cmd->sense[2] &= 0xcf;
@@ -312,6 +314,7 @@ static int tape_done(struct scst_cmd *cmd)
cmd->cdb[1], TransferLength, Residue);
if (TransferLength > Residue) {
int resp_data_len = TransferLength - Residue;
if (cmd->cdb[1] & 1) {
/*
* No need for locks here, since
+332 -14
View File
@@ -70,6 +70,7 @@ struct scst_user_dev {
unsigned int swp:1;
unsigned int d_sense:1;
unsigned int has_own_order_mgmt:1;
unsigned int ext_copy_remap_supported:1;
int (*generic_parse)(struct scst_cmd *cmd);
@@ -260,6 +261,7 @@ static inline bool ucmd_get_check(struct scst_user_cmd *ucmd)
{
int r = atomic_inc_return(&ucmd->ucmd_ref);
int res;
if (unlikely(r == 1)) {
TRACE_DBG("ucmd %p is being destroyed", ucmd);
atomic_dec(&ucmd->ucmd_ref);
@@ -550,6 +552,7 @@ static int dev_user_alloc_sg(struct scst_user_cmd *ucmd, int cached_buff)
} else {
/* Make out_sg->offset 0 */
int len = cmd->bufflen + ucmd->first_page_offset;
out_sg_pages = (len >> PAGE_SHIFT) + ((len & ~PAGE_MASK) != 0);
orig_bufflen = (out_sg_pages << PAGE_SHIFT) + cmd->out_bufflen;
pool = dev->pool;
@@ -611,6 +614,7 @@ static int dev_user_alloc_sg(struct scst_user_cmd *ucmd, int cached_buff)
if (unlikely(cmd->sg_cnt > cmd->tgt_dev->max_sg_cnt)) {
static int ll;
if ((ll < 10) || TRACING_MINOR()) {
PRINT_INFO("Unable to complete command due to "
"SG IO count limitation (requested %d, "
@@ -949,6 +953,37 @@ static int dev_user_exec(struct scst_cmd *cmd)
return res;
}
static void dev_user_ext_copy_remap(struct scst_cmd *cmd,
struct scst_ext_copy_seg_descr *seg)
{
struct scst_user_cmd *ucmd = cmd->dh_priv;
TRACE_ENTRY();
TRACE_DBG("Preparing EXT_COPY_REMAP for user space (ucmd=%p, h=%d, "
"seg %p)", ucmd, ucmd->h, seg);
ucmd->user_cmd_payload_len =
offsetof(struct scst_user_get_cmd, remap_cmd) +
sizeof(ucmd->user_cmd.remap_cmd);
ucmd->user_cmd.cmd_h = ucmd->h;
ucmd->user_cmd.subcode = SCST_USER_EXT_COPY_REMAP;
ucmd->user_cmd.remap_cmd.sess_h = (unsigned long)cmd->tgt_dev;
ucmd->user_cmd.remap_cmd.src_sess_h = (unsigned long)seg->src_tgt_dev;
ucmd->user_cmd.remap_cmd.dst_sess_h = (unsigned long)seg->dst_tgt_dev;
ucmd->user_cmd.remap_cmd.data_descr.src_lba = seg->data_descr.src_lba;
ucmd->user_cmd.remap_cmd.data_descr.dst_lba = seg->data_descr.dst_lba;
ucmd->user_cmd.remap_cmd.data_descr.data_len = seg->data_descr.data_len;
ucmd->state = UCMD_STATE_EXT_COPY_REMAPPING;
dev_user_add_to_ready(ucmd);
TRACE_EXIT();
return;
}
static void dev_user_free_sgv(struct scst_user_cmd *ucmd)
{
if (ucmd->sgv != NULL) {
@@ -1058,6 +1093,7 @@ static void dev_user_set_block_size(struct scst_cmd *cmd, int block_size)
dev->block_size = block_size;
else {
struct scst_user_dev *udev = cmd->dev->dh_priv;
dev->block_size = udev->def_block_size;
}
dev->block_shift = -1; /* not used */
@@ -1180,7 +1216,8 @@ static void dev_user_add_to_ready(struct scst_user_cmd *ucmd)
&dev->ready_cmd_list);
}
do_wake |= ((ucmd->state == UCMD_STATE_ON_CACHE_FREEING) ||
(ucmd->state == UCMD_STATE_ON_FREEING));
(ucmd->state == UCMD_STATE_ON_FREEING) ||
(ucmd->state == UCMD_STATE_EXT_COPY_REMAPPING));
}
if (do_wake) {
@@ -1210,8 +1247,9 @@ static int dev_user_map_buf(struct scst_user_cmd *ucmd, unsigned long ubuff,
ucmd->num_data_pages = num_pg;
ucmd->data_pages = kmalloc(sizeof(*ucmd->data_pages) * ucmd->num_data_pages,
GFP_KERNEL);
ucmd->data_pages = kmalloc_array(ucmd->num_data_pages,
sizeof(*ucmd->data_pages),
GFP_KERNEL);
if (ucmd->data_pages == NULL) {
TRACE(TRACE_OUT_OF_MEM, "Unable to allocate data_pages array "
"(num_data_pages=%d)", ucmd->num_data_pages);
@@ -1278,6 +1316,7 @@ static int dev_user_process_reply_alloc(struct scst_user_cmd *ucmd,
if (likely(reply->alloc_reply.pbuf != 0)) {
int pages;
if (ucmd->buff_cached) {
if (unlikely((reply->alloc_reply.pbuf & ~PAGE_MASK) != 0)) {
PRINT_ERROR("Supplied pbuf %llx isn't "
@@ -1444,6 +1483,254 @@ static int dev_user_process_reply_on_cache_free(struct scst_user_cmd *ucmd)
return res;
}
static int dev_user_process_reply_ext_copy_remap(struct scst_user_cmd *ucmd,
struct scst_user_reply_cmd *reply)
{
int res = 0, rc, count = 0, i, len;
struct scst_user_ext_copy_reply_remap *rreply = &reply->remap_reply;
struct scst_cmd *cmd = ucmd->cmd;
uint8_t *buf;
struct scst_user_ext_copy_data_descr *uleft;
struct scst_ext_copy_data_descr *left = NULL;
TRACE_ENTRY();
if (unlikely((rreply->status != 0) || (rreply->sense_len != 0)))
goto out_status;
if (rreply->remap_descriptors_len == 0) {
TRACE_DBG("No remap leftovers (ucmd %p, cmd %p)", ucmd, cmd);
goto out_done;
}
if (unlikely(rreply->remap_descriptors_len > PAGE_SIZE)) {
PRINT_ERROR("Too many leftover REMAP descriptors (len %d, ucmd %p, "
"cmd %p)", rreply->remap_descriptors_len, ucmd, cmd);
res = -EOVERFLOW;
goto out_hw_err;
}
buf = kzalloc(rreply->remap_descriptors_len, GFP_KERNEL);
if (unlikely(buf == NULL)) {
PRINT_ERROR("Unable to alloc leftover remap descriptors buf "
"(size %d)", rreply->remap_descriptors_len);
goto out_busy;
}
rc = copy_from_user(buf,
(void __user *)(unsigned long)rreply->remap_descriptors,
rreply->remap_descriptors_len);
if (unlikely(rc != 0)) {
PRINT_ERROR("Failed to copy %d leftover remap descriptors' "
"bytes", rc);
res = -EFAULT;
goto out_free_buf;
}
uleft = (struct scst_user_ext_copy_data_descr *)buf;
count = rreply->remap_descriptors_len / sizeof(*uleft);
if (unlikely((rreply->remap_descriptors_len % sizeof(*uleft)) != 0)) {
PRINT_ERROR("Invalid leftover remap descriptors (ucmd %p, "
"cmd %p, count %d)", ucmd, cmd, count);
res = -EINVAL;
goto out_hw_err_free_buf;
}
left = kmalloc_array(count, sizeof(*left), GFP_KERNEL);
if (unlikely(left == NULL)) {
PRINT_ERROR("Unable to alloc leftover remap descriptors "
"(size %zd, count %d)", sizeof(*left) * count, count);
goto out_busy_free_buf;
}
TRACE_DBG("count %d", count);
len = 0;
for (i = 0; i < count; i++) {
TRACE_DBG("src_lba %lld, dst_lba %lld, data_len %d (len %d)",
(long long)uleft[i].src_lba, (long long)uleft[i].dst_lba,
uleft[i].data_len, len);
if (unlikely(uleft[i].data_len == 0)) {
PRINT_ERROR("Invalid data descr %d len %d (cmd %p)", i,
uleft[i].data_len, cmd);
res = -EINVAL;
goto out_hw_err_free_buf;
}
left[i].src_lba = uleft[i].src_lba;
left[i].dst_lba = uleft[i].dst_lba;
left[i].data_len = uleft[i].data_len;
len += uleft[i].data_len;
}
if (unlikely(len > scst_ext_copy_get_cur_seg_data_len(cmd))) {
PRINT_ERROR("Invalid data descr len %d (cmd %p, seg descr len %d)",
len, cmd, scst_ext_copy_get_cur_seg_data_len(cmd));
res = -EINVAL;
goto out_hw_err_free_buf;
}
out_free_buf:
kfree(buf);
out_done:
if (unlikely(res != 0)) {
kfree(left);
left = NULL;
count = 0;
}
scst_ext_copy_remap_done(cmd, left, count);
TRACE_EXIT_RES(res);
return res;
out_hw_err:
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
goto out_done;
out_hw_err_free_buf:
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
goto out_free_buf;
out_busy_free_buf:
scst_set_busy(cmd);
goto out_free_buf;
out_busy:
scst_set_busy(cmd);
goto out_done;
out_status:
TRACE_DBG("Remap finished with status %d (ucmd %p, cmd %p)",
rreply->status, ucmd, cmd);
if (rreply->sense_len != 0) {
int sense_len;
res = scst_alloc_sense(cmd, 0);
if (res != 0)
goto out_hw_err;
sense_len = min_t(int, cmd->sense_buflen, rreply->sense_len);
rc = copy_from_user(cmd->sense,
(void __user *)(unsigned long)rreply->psense_buffer,
sense_len);
if (rc != 0) {
PRINT_ERROR("Failed to copy %d sense's bytes", rc);
res = -EFAULT;
goto out_hw_err;
}
cmd->sense_valid_len = sense_len;
}
scst_set_cmd_error_status(cmd, rreply->status);
goto out_done;
}
static int dev_user_process_ws_reply(struct scst_user_cmd *ucmd,
struct scst_user_scsi_cmd_reply_exec *ereply)
{
int res = 0, rc, count, i;
struct scst_cmd *cmd = ucmd->cmd;
uint8_t *buf;
struct scst_user_data_descriptor *uwhere;
struct scst_data_descriptor *where;
TRACE_ENTRY();
if (unlikely(cmd->cdb[0] != WRITE_SAME) &&
unlikely(cmd->cdb[0] != WRITE_SAME_16)) {
PRINT_ERROR("Request to process WRITE SAME for not WRITE SAME "
"CDB (ucmd %p, cmd %p, op %s)", ucmd, cmd,
scst_get_opcode_name(cmd));
res = -EINVAL;
goto out_hw_err;
}
if (unlikely(ereply->status != 0)) {
PRINT_ERROR("Request to process WRITE SAME with not 0 status "
"(ucmd %p, cmd %p, status %d)", ucmd, cmd, ereply->status);
res = -EINVAL;
goto out_hw_err;
}
if (ereply->ws_descriptors_len == 0) {
scst_write_same(cmd, NULL);
goto out;
}
if (unlikely(ereply->ws_descriptors_len > PAGE_SIZE)) {
PRINT_ERROR("Too many WRITE SAME descriptors (len %d, ucmd %p, "
"cmd %p)", ereply->ws_descriptors_len, ucmd, cmd);
res = -EOVERFLOW;
goto out_hw_err;
}
buf = kzalloc(ereply->ws_descriptors_len, GFP_KERNEL);
if (unlikely(buf == NULL)) {
PRINT_ERROR("Unable to alloc WS descriptors buf (size %d)",
ereply->ws_descriptors_len);
goto out_busy;
}
rc = copy_from_user(buf,
(void __user *)(unsigned long)ereply->ws_descriptors,
ereply->ws_descriptors_len);
if (unlikely(rc != 0)) {
PRINT_ERROR("Failed to copy %d WS descriptors' bytes", rc);
res = -EFAULT;
goto out_free_buf;
}
uwhere = (struct scst_user_data_descriptor *)buf;
count = ereply->ws_descriptors_len / sizeof(*uwhere);
if (unlikely((ereply->ws_descriptors_len % sizeof(*uwhere)) != 0) ||
unlikely(uwhere[count-1].usdd_blocks != 0)) {
PRINT_ERROR("Invalid WS descriptors (ucmd %p, cmd %p)",
ucmd, cmd);
res = -EINVAL;
goto out_free_buf;
}
where = kmalloc_array(count, sizeof(*where), GFP_KERNEL);
if (unlikely(where == NULL)) {
PRINT_ERROR("Unable to alloc WS descriptors where (size %zd)",
sizeof(*where) * count);
goto out_busy_free_buf;
}
for (i = 0; i < count; i++) {
where[i].sdd_lba = uwhere[i].usdd_lba;
where[i].sdd_blocks = uwhere[i].usdd_blocks;
}
kfree(buf);
scst_write_same(cmd, where);
out:
TRACE_EXIT_RES(res);
return res;
out_free_buf:
kfree(buf);
out_hw_err:
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
out_compl:
cmd->completed = 1;
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_DIRECT);
/* !! At this point cmd can be already freed !! */
goto out;
out_busy_free_buf:
kfree(buf);
out_busy:
scst_set_busy(cmd);
goto out_compl;
}
static int dev_user_process_reply_exec(struct scst_user_cmd *ucmd,
struct scst_user_reply_cmd *reply)
{
@@ -1480,6 +1767,9 @@ static int dev_user_process_reply_exec(struct scst_user_cmd *ucmd,
ucmd->background_exec = 1;
TRACE_DBG("Background ucmd %p", ucmd);
goto out_compl;
} else if (ereply->reply_type == SCST_EXEC_REPLY_DO_WRITE_SAME) {
res = dev_user_process_ws_reply(ucmd, ereply);
goto out;
} else
goto out_inval;
@@ -1491,6 +1781,7 @@ static int dev_user_process_reply_exec(struct scst_user_cmd *ucmd,
if (ereply->resp_data_len != 0) {
if (ucmd->ubuff == 0) {
int pages, rc;
if (unlikely(ereply->pbuf == 0))
goto out_busy;
if (ucmd->buff_cached) {
@@ -1657,6 +1948,10 @@ unlock_process:
res = dev_user_process_reply_on_cache_free(ucmd);
break;
case UCMD_STATE_EXT_COPY_REMAPPING:
res = dev_user_process_reply_ext_copy_remap(ucmd, reply);
break;
case UCMD_STATE_TM_RECEIVED_EXECING:
case UCMD_STATE_TM_DONE_EXECING:
res = dev_user_process_reply_tm_exec(ucmd,
@@ -2036,6 +2331,7 @@ static int dev_user_reply_get_cmd(struct file *file, void __user *arg)
if (ureply != 0) {
unsigned long u = (unsigned long)ureply;
rc = copy_from_user(&reply, (void __user *)u, sizeof(reply));
if (unlikely(rc != 0)) {
PRINT_ERROR("Failed to copy %d user's bytes", rc);
@@ -2188,6 +2484,7 @@ static long dev_user_ioctl(struct file *file, unsigned int cmd,
case SCST_USER_REGISTER_DEVICE:
{
struct scst_user_dev_desc *dev_desc;
TRACE_DBG("%s", "REGISTER_DEVICE");
dev_desc = kmalloc(sizeof(*dev_desc), GFP_KERNEL);
if (dev_desc == NULL) {
@@ -2223,6 +2520,7 @@ static long dev_user_ioctl(struct file *file, unsigned int cmd,
case SCST_USER_SET_OPTIONS:
{
struct scst_user_opt opt;
TRACE_DBG("%s", "SET_OPTIONS");
rc = copy_from_user(&opt, (void __user *)arg, sizeof(opt));
if (rc != 0) {
@@ -2353,6 +2651,7 @@ static void dev_user_unjam_cmd(struct scst_user_cmd *ucmd, int busy,
break;
case UCMD_STATE_EXECING:
case UCMD_STATE_EXT_COPY_REMAPPING:
if (flags != NULL)
spin_unlock_irqrestore(&dev->udev_cmd_threads.cmd_list_lock,
*flags);
@@ -2371,8 +2670,13 @@ static void dev_user_unjam_cmd(struct scst_user_cmd *ucmd, int busy,
SCST_LOAD_SENSE(scst_sense_lun_not_supported));
}
ucmd->cmd->scst_cmd_done(ucmd->cmd, SCST_CMD_STATE_DEFAULT,
SCST_CONTEXT_THREAD);
if (state == UCMD_STATE_EXECING)
ucmd->cmd->scst_cmd_done(ucmd->cmd, SCST_CMD_STATE_DEFAULT,
SCST_CONTEXT_THREAD);
else {
sBUG_ON(state != UCMD_STATE_EXT_COPY_REMAPPING);
scst_ext_copy_remap_done(ucmd->cmd, NULL, 0);
}
/* !! At this point cmd and ucmd can be already freed !! */
if (flags != NULL)
@@ -2911,6 +3215,11 @@ static void dev_user_setup_functions(struct scst_user_dev *dev)
dev->devtype.dev_alloc_data_buf = dev_user_alloc_data_buf;
dev->devtype.dev_done = NULL;
if (dev->ext_copy_remap_supported)
dev->devtype.ext_copy_remap = dev_user_ext_copy_remap;
else
dev->devtype.ext_copy_remap = NULL;
if (dev->parse_type != SCST_USER_PARSE_CALL) {
switch (dev->devtype.type) {
case TYPE_DISK:
@@ -3364,9 +3673,10 @@ static int __dev_user_set_opt(struct scst_user_dev *dev,
TRACE_DBG("dev %s, parse_type %x, on_free_cmd_type %x, "
"memory_reuse_type %x, partial_transfers_type %x, "
"partial_len %d", dev->name, opt->parse_type,
opt->on_free_cmd_type, opt->memory_reuse_type,
opt->partial_transfers_type, opt->partial_len);
"partial_len %d, opt->ext_copy_remap_supported %d",
dev->name, opt->parse_type, opt->on_free_cmd_type,
opt->memory_reuse_type, opt->partial_transfers_type,
opt->partial_len, opt->ext_copy_remap_supported);
if (opt->parse_type > SCST_USER_MAX_PARSE_OPT ||
opt->on_free_cmd_type > SCST_USER_MAX_ON_FREE_CMD_OPT ||
@@ -3385,12 +3695,13 @@ static int __dev_user_set_opt(struct scst_user_dev *dev,
((opt->qerr == SCST_QERR_2_RESERVED) ||
(opt->qerr > SCST_QERR_3_ABORT_THIS_NEXUS_ONLY)) ||
(opt->swp > 1) || (opt->tas > 1) || (opt->has_own_order_mgmt > 1) ||
(opt->d_sense > 1)) {
(opt->d_sense > 1) || (opt->ext_copy_remap_supported > 1)) {
PRINT_ERROR("Invalid SCSI option (tst %x, tmf_only %x, "
"queue_alg %x, qerr %x, swp %x, tas %x, d_sense %d, "
"has_own_order_mgmt %x)",
"has_own_order_mgmt %x, ext_copy_remap_supported %d)",
opt->tst, opt->tmf_only, opt->queue_alg, opt->qerr,
opt->swp, opt->tas, opt->d_sense, opt->has_own_order_mgmt);
opt->swp, opt->tas, opt->d_sense, opt->has_own_order_mgmt,
opt->ext_copy_remap_supported);
res = -EINVAL;
goto out;
}
@@ -3419,6 +3730,7 @@ static int __dev_user_set_opt(struct scst_user_dev *dev,
dev->tas = opt->tas;
dev->d_sense = opt->d_sense;
dev->has_own_order_mgmt = opt->has_own_order_mgmt;
dev->ext_copy_remap_supported = opt->ext_copy_remap_supported;
if (dev->sdev != NULL) {
dev->sdev->tst = opt->tst;
dev->sdev->tmf_only = opt->tmf_only;
@@ -3475,6 +3787,7 @@ static int dev_user_get_opt(struct file *file, void __user *arg)
if (unlikely(res != 0))
goto out;
memset(&opt, 0, sizeof(opt));
opt.parse_type = dev->parse_type;
opt.on_free_cmd_type = dev->on_free_cmd_type;
opt.memory_reuse_type = dev->memory_reuse_type;
@@ -3488,12 +3801,14 @@ static int dev_user_get_opt(struct file *file, void __user *arg)
opt.swp = dev->swp;
opt.d_sense = dev->d_sense;
opt.has_own_order_mgmt = dev->has_own_order_mgmt;
opt.ext_copy_remap_supported = dev->ext_copy_remap_supported;
TRACE_DBG("dev %s, parse_type %x, on_free_cmd_type %x, "
"memory_reuse_type %x, partial_transfers_type %x, "
"partial_len %d", dev->name, opt.parse_type,
opt.on_free_cmd_type, opt.memory_reuse_type,
opt.partial_transfers_type, opt.partial_len);
"partial_len %d, ext_copy_remap_supported %d", dev->name,
opt.parse_type, opt.on_free_cmd_type, opt.memory_reuse_type,
opt.partial_transfers_type, opt.partial_len,
opt.ext_copy_remap_supported);
rc = copy_to_user(arg, &opt, sizeof(opt));
if (unlikely(rc != 0)) {
@@ -3563,6 +3878,7 @@ static int dev_user_exit_dev(struct scst_user_dev *dev)
static int __dev_user_release(void *arg)
{
struct scst_user_dev *dev = arg;
dev_user_exit_dev(dev);
kmem_cache_free(user_dev_cachep, dev);
return 0;
@@ -3633,6 +3949,7 @@ static int dev_user_process_cleanup(struct scst_user_dev *dev)
#ifdef CONFIG_SCST_EXTRACHECKS
{
int i;
for (i = 0; i < (int)ARRAY_SIZE(dev->ucmd_hash); i++) {
struct list_head *head = &dev->ucmd_hash[i];
struct scst_user_cmd *ucmd2, *tmp;
@@ -3674,6 +3991,7 @@ static ssize_t dev_user_sysfs_commands_show(struct kobject *kobj,
for (i = 0; i < (int)ARRAY_SIZE(udev->ucmd_hash); i++) {
struct list_head *head = &udev->ucmd_hash[i];
struct scst_user_cmd *ucmd;
list_for_each_entry(ucmd, head, hash_list_entry) {
ppos = pos;
pos += scnprintf(&buf[pos],
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+4
View File
@@ -232,6 +232,10 @@ const char *debug_transport_id_to_initiator_name(const uint8_t *transport_id)
transport_id[8], transport_id[9],
transport_id[10], transport_id[11]);
break;
case SCST_TRANSPORTID_PROTOCOLID_COPY_MGR:
scnprintf(name_buf, SIZEOF_NAME_BUF,
"%s", &transport_id[2]);
break;
default:
scnprintf(name_buf, SIZEOF_NAME_BUF,
"(Not known protocol ID %x)", transport_id[0] & 0x0f);
File diff suppressed because it is too large Load Diff
+1375 -163
View File
File diff suppressed because it is too large Load Diff
+47 -2
View File
@@ -596,6 +596,7 @@ EXPORT_SYMBOL(scst_register_target);
static inline int test_sess_list(struct scst_tgt *tgt)
{
int res;
mutex_lock(&scst_mutex);
res = list_empty(&tgt->sysfs_sess_list);
mutex_unlock(&scst_mutex);
@@ -637,6 +638,7 @@ void scst_unregister_target(struct scst_tgt *tgt)
again:
{
struct scst_session *sess;
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
if (sess->shut_phase == SCST_SESS_SPH_READY) {
/*
@@ -870,6 +872,7 @@ static void __printf(2, 3) scst_to_syslog(void *arg, const char *fmt, ...)
int scst_get_cmd_counter(void)
{
int i, res = 0;
for (i = 0; i < (int)ARRAY_SIZE(scst_percpu_infos); i++)
res += atomic_read(&scst_percpu_infos[i].cpu_cmd_count);
return res;
@@ -1467,6 +1470,10 @@ int scst_register_virtual_device(struct scst_dev_type *dev_handler,
list_add_tail(&dev->dev_list_entry, &scst_dev_list);
res = scst_cm_on_dev_register(dev);
if (res != 0)
goto out_unreg;
mutex_unlock(&scst_mutex);
scst_resume_activity();
@@ -1478,6 +1485,12 @@ out:
TRACE_EXIT_RES(res);
return res;
out_unreg:
dev->dev_unregistering = 1;
list_del(&dev->dev_list_entry);
scst_assign_dev_handler(dev, &scst_null_devtype);
goto out_pr_clear_dev;
#ifndef CONFIG_SCST_PROC
out_lock_pr_clear_dev:
mutex_lock(&scst_mutex);
@@ -1528,6 +1541,10 @@ void scst_unregister_virtual_device(int id)
goto out_unlock;
}
dev->dev_unregistering = 1;
scst_cm_on_dev_unregister(dev);
list_del_init(&dev->dev_list_entry);
scst_pr_clear_dev(dev);
@@ -1861,6 +1878,7 @@ int scst_add_threads(struct scst_cmd_threads *cmd_threads,
if (tgt_dev != NULL) {
struct scst_tgt_dev *t;
list_for_each_entry(t, &tgt_dev->dev->dev_tgt_dev_list,
dev_tgt_dev_list_entry) {
if (t == tgt_dev)
@@ -2431,7 +2449,8 @@ static int __init init_scst(void)
struct scsi_sense_hdr *shdr;
struct scst_order_data *o;
struct scst_cmd *c;
BUILD_BUG_ON(SCST_SENSE_BUFFERSIZE < sizeof(*shdr));
BUILD_BUG_ON(sizeof(*shdr) > SCST_SENSE_BUFFERSIZE);
BUILD_BUG_ON(sizeof(o->curr_sn) != sizeof(o->expected_sn));
BUILD_BUG_ON(sizeof(c->sn) != sizeof(o->expected_sn));
}
@@ -2578,14 +2597,19 @@ static int __init init_scst(void)
goto out_destroy_sense_mempool;
}
res = scst_sysfs_init();
res = scst_event_init();
if (res != 0)
goto out_destroy_aen_mempool;
res = scst_sysfs_init();
if (res != 0)
goto out_event_exit;
scst_tg_init();
if (scst_max_cmd_mem == 0) {
struct sysinfo si;
si_meminfo(&si);
#if BITS_PER_LONG == 32
scst_max_cmd_mem = min(
@@ -2651,6 +2675,15 @@ static int __init init_scst(void)
goto out_thread_free;
#endif
res = scst_cm_init();
if (res != 0)
#ifdef CONFIG_SCST_PROC
goto out_proc_cleanup;
#else
goto out_thread_free;
#endif
PRINT_INFO("SCST version %s loaded successfully (max mem for "
"commands %dMB, per device %dMB)", SCST_VERSION_STRING,
scst_max_cmd_mem, scst_max_dev_cmd_mem);
@@ -2661,6 +2694,11 @@ out:
TRACE_EXIT_RES(res);
return res;
#ifdef CONFIG_SCST_PROC
out_proc_cleanup:
scst_proc_cleanup_module();
#endif
out_thread_free:
scst_stop_global_threads();
@@ -2678,6 +2716,9 @@ out_destroy_sgv_pool:
out_sysfs_cleanup:
scst_sysfs_cleanup();
out_event_exit:
scst_event_exit();
out_destroy_aen_mempool:
mempool_destroy(scst_aen_mempool);
@@ -2740,6 +2781,8 @@ static void __exit exit_scst(void)
/* ToDo: unregister_cpu_notifier() */
scst_cm_exit();
#ifdef CONFIG_SCST_PROC
scst_proc_cleanup_module();
#endif
@@ -2759,6 +2802,8 @@ static void __exit exit_scst(void)
scst_sysfs_cleanup();
scst_event_exit();
#define DEINIT_CACHEP(p) do { \
kmem_cache_destroy(p); \
p = NULL; \
+6 -1
View File
@@ -675,6 +675,7 @@ static int sgv_alloc_sg_entries(struct scatterlist *sg, int pages,
pg = 0;
for (i = 0; i < pages; i++) {
int n = PAGE_ALIGN(sg[i].length) >> PAGE_SHIFT;
trans_tbl[i].pg_count = pg;
for (j = 0; j < n; j++)
trans_tbl[pg++].sg_num = i+1;
@@ -972,6 +973,7 @@ struct scatterlist *sgv_pool_alloc(struct sgv_pool *pool, unsigned int size,
pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
if (pool->single_alloc_pages == 0) {
int pages_order = get_order(size);
cache_num = pages_order;
pages_to_alloc = (1 << pages_order);
} else {
@@ -1128,6 +1130,7 @@ struct scatterlist *sgv_pool_alloc(struct sgv_pool *pool, unsigned int size,
success:
if (cache_num >= 0) {
int sg;
atomic_inc(&pool->cache_acc[cache_num].total_alloc);
if (sgv_pool_clustered(pool))
cnt = obj->trans_tbl[pages-1].sg_num;
@@ -1255,10 +1258,12 @@ void sgv_pool_free(struct sgv_pool_obj *obj, struct scst_mem_lim *mem_lim)
{
struct scatterlist *sg = obj->sg_entries;
int i;
for (i = 0; i < obj->sg_count; i++) {
struct page *p = sg_page(&sg[i]);
int len = sg[i].length;
int pages = PAGE_ALIGN(len) >> PAGE_SHIFT;
while (pages > 0) {
if (page_count(p) != 1) {
PRINT_WARNING("Freeing page %p with "
@@ -1327,7 +1332,7 @@ struct scatterlist *scst_alloc_sg(int size, gfp_t gfp_mask, int *count)
}
}
res = kmalloc(pages*sizeof(*res), gfp_mask);
res = kmalloc_array(pages, sizeof(*res), gfp_mask);
if (res == NULL) {
TRACE(TRACE_OUT_OF_MEM, "Unable to allocate sg for %d pages",
pages);
+11 -5
View File
@@ -103,6 +103,7 @@ static inline void tid_secure(uint8_t *tid)
{
if ((tid[0] & 0x0f) == SCSI_TRANSPORTID_PROTOCOLID_ISCSI) {
int size = scst_tid_size(tid);
tid[size - 1] = '\0';
}
@@ -110,7 +111,7 @@ static inline void tid_secure(uint8_t *tid)
}
/* Returns false if tid's are not equal, true otherwise */
static bool tid_equal(const uint8_t *tid_a, const uint8_t *tid_b)
bool tid_equal(const uint8_t *tid_a, const uint8_t *tid_b)
{
int len;
@@ -137,6 +138,7 @@ static bool tid_equal(const uint8_t *tid_a, const uint8_t *tid_b)
else if (tid_a_fmt == 0x40) {
if (tid_a_fmt != tid_b_fmt) {
uint8_t *p = strnchr(tid_a, tid_a_max, ',');
if (p == NULL)
goto out_error;
tid_a_len = p - tid_a;
@@ -153,6 +155,7 @@ static bool tid_equal(const uint8_t *tid_a, const uint8_t *tid_b)
else if (tid_b_fmt == 0x40) {
if (tid_a_fmt != tid_b_fmt) {
uint8_t *p = strnchr(tid_b, tid_b_max, ',');
if (p == NULL)
goto out_error;
tid_b_len = p - tid_b;
@@ -243,6 +246,7 @@ void scst_pr_dump_prs(struct scst_device *dev, bool force)
else {
struct scst_dev_registrant *reg;
int i = 0;
list_for_each_entry(reg, &dev->dev_registrants_list,
dev_registrants_list_entry) {
PRINT_INFO(" [%d] registrant %s/%d, key %016llx "
@@ -256,6 +260,7 @@ void scst_pr_dump_prs(struct scst_device *dev, bool force)
if (dev->pr_is_set) {
struct scst_dev_registrant *holder = dev->pr_holder;
if (holder != NULL)
PRINT_INFO("Reservation holder is %s/%d (key %016llx, "
"scope %x, type %x, reg %p, tgt_dev %p)",
@@ -629,6 +634,7 @@ static void scst_pr_abort_reg(struct scst_device *dev,
if ((reg->tgt_dev != pr_cmd->tgt_dev) && !dev->tas) {
uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN];
int sl;
sl = scst_set_sense(sense_buffer, sizeof(sense_buffer),
dev->d_sense,
SCST_LOAD_SENSE(scst_sense_cleared_by_another_ini_UA));
@@ -688,11 +694,11 @@ static int scst_pr_do_load_device_file(struct scst_device *dev,
inode = file_inode(file);
if (S_ISREG(inode->i_mode))
/* Nothing to do */;
else if (S_ISBLK(inode->i_mode))
if (S_ISREG(inode->i_mode)) {
/* Nothing to do */
} else if (S_ISBLK(inode->i_mode)) {
inode = inode->i_bdev->bd_inode;
else {
} else {
PRINT_ERROR("Invalid file mode 0x%x", inode->i_mode);
goto out_close;
}
+2
View File
@@ -131,6 +131,8 @@ void scst_pr_read_full_status(struct scst_cmd *cmd, uint8_t *buffer,
int buffer_size);
int scst_tid_size(const uint8_t *tid);
bool tid_equal(const uint8_t *tid_a, const uint8_t *tid_b);
struct scst_dev_registrant *scst_pr_find_reg(struct scst_device *dev,
const uint8_t *transport_id, const uint16_t rel_tgt_id);
struct scst_dev_registrant *scst_pr_add_registrant(struct scst_device *dev,
+106 -4
View File
@@ -458,7 +458,6 @@ struct scst_dev_group *scst_lookup_dg_by_kobj(struct kobject *kobj);
int scst_dg_dev_add(struct scst_dev_group *dg, const char *name);
int scst_dg_dev_remove_by_name(struct scst_dev_group *dg, const char *name);
int scst_dg_dev_remove_by_dev(struct scst_device *dev);
const char *scst_alua_state_name(enum scst_tg_state s);
enum scst_tg_state scst_alua_name_to_state(const char *n);
int scst_tg_add(struct scst_dev_group *dg, const char *name);
int scst_tg_remove_by_name(struct scst_dev_group *dg, const char *name);
@@ -716,10 +715,18 @@ void scst_gen_aen_or_ua(struct scst_tgt_dev *tgt_dev,
* we don't care about all commands that already on the device.
*/
extern void scst_block_dev(struct scst_device *dev);
extern void scst_unblock_dev(struct scst_device *dev);
void scst_block_dev(struct scst_device *dev);
void scst_unblock_dev(struct scst_device *dev);
bool scst_do_check_blocked_dev(struct scst_cmd *cmd);
bool __scst_check_blocked_dev(struct scst_cmd *cmd);
void __scst_check_unblock_dev(struct scst_cmd *cmd);
void scst_check_unblock_dev(struct scst_cmd *cmd);
int scst_ext_block_dev(struct scst_device *dev, bool sync,
ext_blocker_done_fn_t done_fn, const uint8_t *priv, int priv_len);
void scst_ext_unblock_dev(struct scst_device *dev, bool stpg);
void __scst_ext_blocking_done(struct scst_device *dev);
void scst_ext_blocking_done(struct scst_device *dev);
/*
* Increases global SCST ref counters which prevent from entering into suspended
@@ -758,6 +765,7 @@ static inline atomic_t *scst_get(void)
static inline void scst_put(atomic_t *a)
{
int f;
f = atomic_dec_and_test(a);
/* See comment about smp_mb() in scst_suspend_activity() */
if (unlikely(test_bit(SCST_FLAG_SUSPENDED, &scst_flags)) && f) {
@@ -815,6 +823,8 @@ void scst_unthrottle_cmd(struct scst_cmd *cmd);
int scst_do_internal_parsing(struct scst_cmd *cmd);
int scst_parse_descriptors(struct scst_cmd *cmd);
int scst_cmp_wr_local(struct scst_cmd *cmd);
int scst_pr_init(struct scst_device *dev);
void scst_pr_cleanup(struct scst_device *dev);
@@ -826,6 +836,87 @@ void scst_vfs_unlink_and_put(struct path *path);
int scst_copy_file(const char *src, const char *dest);
struct scst_cmd *__scst_create_prepare_internal_cmd(const uint8_t *cdb,
unsigned int cdb_len, enum scst_cmd_queue_type queue_type,
struct scst_tgt_dev *tgt_dev, gfp_t gfp_mask, bool fantom);
static inline bool scst_lba1_inside_lba2(int64_t lba1,
int64_t lba2, int64_t lba2_blocks)
{
bool res;
TRACE_DBG("lba1 %lld, lba2 %lld, lba2_blocks %lld", (long long)lba1,
(long long)lba2, (long long)lba2_blocks);
if ((lba1 >= lba2) && (lba1 < (lba2 + lba2_blocks)))
res = true;
else
res = false;
TRACE_EXIT_RES(res);
return res;
}
#ifndef CONFIG_SCST_PROC
int scst_cm_on_dev_register(struct scst_device *dev);
void scst_cm_on_dev_unregister(struct scst_device *dev);
int scst_cm_parse_descriptors(struct scst_cmd *cmd);
void scst_cm_free_descriptors(struct scst_cmd *cmd);
int scst_cm_ext_copy_exec(struct scst_cmd *cmd);
int scst_cm_rcv_copy_res_exec(struct scst_cmd *cmd);
void sess_cm_list_id_cleanup_work_fn(struct delayed_work *work);
void scst_cm_free_pending_list_ids(struct scst_session *sess);
bool scst_cm_check_block_all_devs(struct scst_cmd *cmd);
void scst_cm_abort_ec_cmd(struct scst_cmd *ec_cmd);
bool scst_cm_ec_cmd_overlap(struct scst_cmd *ec_cmd, struct scst_cmd *cmd);
int scst_cm_init(void);
void scst_cm_exit(void);
#else /* #ifndef CONFIG_SCST_PROC */
static inline int scst_cm_on_dev_register(struct scst_device *dev) { return 0; }
static inline void scst_cm_on_dev_unregister(struct scst_device *dev) {}
static inline int scst_cm_parse_descriptors(struct scst_cmd *cmd)
{
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_opcode));
scst_set_cmd_abnormal_done_state(cmd);
return -1;
}
static inline void scst_cm_free_descriptors(struct scst_cmd *cmd) {}
static inline int scst_cm_ext_copy_exec(struct scst_cmd *cmd)
{
return SCST_EXEC_NOT_COMPLETED;
}
static inline int scst_cm_rcv_copy_res_exec(struct scst_cmd *cmd)
{
return SCST_EXEC_NOT_COMPLETED;
}
static inline void sess_cm_list_id_cleanup_work_fn(struct work_struct *work) {}
static inline void scst_cm_free_pending_list_ids(struct scst_session *sess) {}
static inline bool scst_cm_check_block_all_devs(struct scst_cmd *cmd) { return false; }
static inline void scst_cm_abort_ec_cmd(struct scst_cmd *ec_cmd) {}
static inline bool scst_cm_ec_cmd_overlap(struct scst_cmd *ec_cmd, struct scst_cmd *cmd)
{
return false;
}
static inline int scst_cm_init(void) { return 0; }
static inline void scst_cm_exit(void) {}
#endif /* #ifndef CONFIG_SCST_PROC */
#ifdef CONFIG_SCST_DEBUG_TM
extern void tm_dbg_check_released_cmds(void);
extern int tm_dbg_check_cmd(struct scst_cmd *cmd);
@@ -863,6 +954,15 @@ int gen_relative_target_port_id(uint16_t *id);
bool scst_is_relative_target_port_id_unique(uint16_t id,
const struct scst_tgt *t);
int scst_event_init(void);
void scst_event_exit(void);
int scst_event_queue_lun_not_found(const struct scst_cmd *cmd);
int scst_event_queue_negative_luns_inquiry(const struct scst_tgt *tgt,
const char *initiator_name);
int scst_event_queue_ext_blocking_done(struct scst_device *dev, void *data, int len);
int scst_event_queue_tm_fn_received(struct scst_mgmt_cmd *mcmd);
typedef void __printf(2, 3) (*scst_show_fn)(void *arg, const char *fmt, ...);
void scst_trace_cmds(scst_show_fn show, void *arg);
void scst_trace_mcmds(scst_show_fn show, void *arg);
@@ -876,6 +976,7 @@ void scst_set_alloc_buf_time(struct scst_cmd *cmd);
void scst_set_restart_waiting_time(struct scst_cmd *cmd);
void scst_set_rdy_to_xfer_time(struct scst_cmd *cmd);
void scst_set_pre_exec_time(struct scst_cmd *cmd);
void scst_set_exec_start(struct scst_cmd *cmd);
void scst_set_exec_time(struct scst_cmd *cmd);
void scst_set_dev_done_time(struct scst_cmd *cmd);
void scst_set_xmit_time(struct scst_cmd *cmd);
@@ -890,6 +991,7 @@ static inline void scst_set_alloc_buf_time(struct scst_cmd *cmd) {}
static inline void scst_set_restart_waiting_time(struct scst_cmd *cmd) {}
static inline void scst_set_rdy_to_xfer_time(struct scst_cmd *cmd) {}
static inline void scst_set_pre_exec_time(struct scst_cmd *cmd) {}
static inline void scst_set_exec_start(struct scst_cmd *cmd) {}
static inline void scst_set_exec_time(struct scst_cmd *cmd) {}
static inline void scst_set_dev_done_time(struct scst_cmd *cmd) {}
static inline void scst_set_xmit_time(struct scst_cmd *cmd) {}
+149 -58
View File
@@ -272,11 +272,7 @@ static int scst_write_trace(const char *buf, size_t length,
}
break;
case SCST_TRACE_ACTION_VALUE:
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
res = kstrtoul(p, 0, &level);
#else
res = strict_strtoul(p, 0, &level);
#endif
if (res != 0) {
PRINT_ERROR("Invalid trace value \"%s\"", p);
res = -EINVAL;
@@ -668,9 +664,11 @@ static int scst_check_tgt_acg_ptrs(struct scst_tgt *tgt, struct scst_acg *acg)
list_for_each_entry(tgtt, &scst_template_list, scst_template_list_entry) {
struct scst_tgt *t;
list_for_each_entry(t, &tgtt->tgt_list, tgt_list_entry) {
if (t == tgt) {
struct scst_acg *a;
if (acg == NULL)
goto out;
if (acg == tgt->default_acg)
@@ -783,8 +781,8 @@ static ssize_t scst_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct kobj_attribute *kobj_attr;
kobj_attr = container_of(attr, struct kobj_attribute, attr);
kobj_attr = container_of(attr, struct kobj_attribute, attr);
return kobj_attr->show(kobj, kobj_attr, buf);
}
@@ -792,8 +790,8 @@ static ssize_t scst_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t count)
{
struct kobj_attribute *kobj_attr;
kobj_attr = container_of(attr, struct kobj_attribute, attr);
kobj_attr = container_of(attr, struct kobj_attribute, attr);
if (kobj_attr->store)
return kobj_attr->store(kobj, kobj_attr, buf, count);
else
@@ -1073,6 +1071,7 @@ static ssize_t scst_tgtt_dif_capable_show(struct kobject *kobj,
if (tgtt->supported_dif_block_sizes) {
const int *p = tgtt->supported_dif_block_sizes;
int j;
pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos,
"Supported blocks: ");
j = pos;
@@ -1445,11 +1444,7 @@ static int __scst_process_luns_mgmt_store(char *buffer,
break;
case SCST_LUN_ACTION_DEL:
p = scst_get_next_lexem(&pp);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
res = kstrtoul(p, 0, &virt_lun);
#else
res = strict_strtoul(p, 0, &virt_lun);
#endif
if (res != 0)
goto out_unlock;
@@ -1758,11 +1753,7 @@ static ssize_t __scst_acg_io_grouping_type_store(struct scst_acg *acg,
min_t(int, strlen(SCST_IO_GROUPING_NEVER_STR), count)) == 0)
io_grouping_type = SCST_IO_GROUPING_NEVER;
else {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
res = kstrtol(buf, 0, &io_grouping_type);
#else
res = strict_strtol(buf, 0, &io_grouping_type);
#endif
if ((res != 0) || (io_grouping_type <= 0)) {
PRINT_ERROR("Unknown or not allowed I/O grouping type "
"%s", buf);
@@ -1892,6 +1883,7 @@ static ssize_t __scst_acg_black_hole_store(struct scst_acg *acg,
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
if (t != SCST_ACG_BLACK_HOLE_NONE)
@@ -2472,11 +2464,7 @@ static ssize_t scst_rel_tgt_id_store(struct kobject *kobj,
tgt = container_of(kobj, struct scst_tgt, tgt_kobj);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
res = kstrtoul(buf, 0, &rel_tgt_id);
#else
res = strict_strtoul(buf, 0, &rel_tgt_id);
#endif
if (res != 0) {
PRINT_ERROR("%s", "Wrong rel_tgt_id");
res = -EINVAL;
@@ -2564,9 +2552,11 @@ static ssize_t scst_tgt_forwarding_store(struct kobject *kobj,
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
int i;
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry(tgt_dev, head, sess_tgt_dev_list_entry) {
if (tgt->tgt_forwarding)
set_bit(SCST_TGT_DEV_FORWARDING, &tgt_dev->tgt_dev_flags);
@@ -2729,6 +2719,7 @@ static ssize_t scst_tgt_dif_capable_show(struct kobject *kobj,
if (tgt->tgt_supported_dif_block_sizes) {
const int *p = tgt->tgt_supported_dif_block_sizes;
int j;
pos += scnprintf(&buf[pos], SCST_SYSFS_BLOCK_SIZE - pos,
"Supported blocks: ");
j = pos;
@@ -3339,13 +3330,9 @@ static ssize_t scst_dev_sysfs_threads_num_store(struct kobject *kobj,
dev = container_of(kobj, struct scst_device, dev_kobj);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
res = kstrtol(buf, 0, &newtn);
#else
res = strict_strtol(buf, 0, &newtn);
#endif
if (res != 0) {
PRINT_ERROR("strict_strtol() for %s failed: %d ", buf, res);
PRINT_ERROR("kstrtol() for %s failed: %d ", buf, res);
goto out;
}
if (newtn < 0) {
@@ -3473,8 +3460,137 @@ static struct kobj_attribute dev_threads_pool_type_attr =
scst_dev_sysfs_threads_pool_type_show,
scst_dev_sysfs_threads_pool_type_store);
static ssize_t scst_dev_block_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int pos = 0;
struct scst_device *dev;
TRACE_ENTRY();
dev = container_of(kobj, struct scst_device, dev_kobj);
pos = sprintf(buf, "%d %d\n", ACCESS_ONCE(dev->ext_blocks_cnt),
dev->ext_blocking_pending);
TRACE_EXIT_RES(pos);
return pos;
}
static void scst_sysfs_ext_blocking_done(struct scst_device *dev,
uint8_t *data, int len)
{
scst_event_queue_ext_blocking_done(dev, data, len);
}
static ssize_t scst_dev_block_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int res, data_len = 0, pos = 0;
struct scst_device *dev;
const char *p = buf, *data_start = NULL;
bool sync;
TRACE_ENTRY();
dev = container_of(kobj, struct scst_device, dev_kobj);
switch (*p) {
case '0':
p++;
pos++;
while ((pos < count) && isspace(*p) && (*p != '\0')) {
p++;
pos++;
}
if ((pos != count) && (*p != '\0')) {
PRINT_ERROR("Parse error on %c", *p);
res = -EINVAL;
goto out;
}
TRACE_MGMT_DBG("Sysfs unblocking (dev %s)", dev->virt_name);
scst_ext_unblock_dev(dev, false);
res = 0;
goto out;
case '1':
p++;
pos++;
while ((pos < count) && isspace(*p) && (*p != '\0')) {
p++;
pos++;
}
if ((pos == count) || (*p == '\0')) {
data_len = sizeof(void *);
sync = true;
break;
} else if (*p != '1') {
PRINT_ERROR("Parse error on %c", *p);
res = -EINVAL;
goto out;
}
sync = false;
p++;
pos++;
if ((pos == count) || (*p == '\0'))
break;
while ((pos < count) && isspace(*p) && (*p != '\0')) {
p++;
pos++;
}
if ((pos == count) || (*p == '\0'))
break;
data_start = p;
while ((pos < count) && (*p != '\0')) {
p++;
pos++;
data_len++;
}
/* Skip trailing spaces, if any */
while (isspace(*(p-1))) {
p--;
data_len--;
}
break;
default:
PRINT_ERROR("Illegal blocking value %c", *p);
res = -EINVAL;
goto out;
}
TRACE_MGMT_DBG("Sysfs blocking dev %s (sync %d, data_start %p, "
"data_len %d)", dev->virt_name, sync, data_start, data_len);
if (sync)
res = scst_ext_block_dev(dev, true, NULL, NULL, 0);
else
res = scst_ext_block_dev(dev, false, scst_sysfs_ext_blocking_done,
data_start, data_len);
if (res != 0)
goto out;
res = 0;
out:
if (res == 0)
res = count;
TRACE_EXIT_RES(res);
return res;
}
static struct kobj_attribute dev_block_attr =
__ATTR(block, S_IRUGO | S_IWUSR, scst_dev_block_show,
scst_dev_block_store);
static struct attribute *scst_dev_attrs[] = {
&dev_type_attr.attr,
&dev_block_attr.attr,
NULL,
};
@@ -3771,11 +3887,7 @@ static ssize_t scst_dev_sysfs_dif_static_app_tag_store(struct kobject *kobj,
dev = container_of(kobj, struct scst_device, dev_kobj);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
res = kstrtoull(buf, 0, &val);
#else
res = strict_strtoull(buf, 0, &val);
#endif
if (res != 0) {
PRINT_ERROR("strtoul() for %s failed: %d (device %s)",
buf, res, dev->virt_name);
@@ -4333,6 +4445,7 @@ static int scst_sess_zero_latency(struct scst_sysfs_work_item *work)
for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) {
struct list_head *head = &sess->sess_tgt_dev_list[t];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
tgt_dev->scst_time = 0;
@@ -4412,6 +4525,7 @@ static int scst_sysfs_sess_get_active_commands(struct scst_session *sess)
for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) {
struct list_head *head = &sess->sess_tgt_dev_list[t];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
active_cmds += atomic_read(&tgt_dev->tgt_dev_cmd_count);
@@ -4477,6 +4591,7 @@ static int scst_sysfs_sess_get_dif_checks_failed_work_fn(struct scst_sysfs_work_
for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) {
struct list_head *head = &sess->sess_tgt_dev_list[t];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry_rcu(tgt_dev, head, sess_tgt_dev_list_entry) {
app_failed_tgt += atomic_read(&tgt_dev->tgt_dev_dif_app_failed_tgt);
ref_failed_tgt += atomic_read(&tgt_dev->tgt_dev_dif_ref_failed_tgt);
@@ -4552,6 +4667,7 @@ static int scst_sess_zero_dif_checks_failed(struct scst_sysfs_work_item *work)
for (t = SESS_TGT_DEV_LIST_HASH_SIZE-1; t >= 0; t--) {
struct list_head *head = &sess->sess_tgt_dev_list[t];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
atomic_set(&tgt_dev->tgt_dev_dif_app_failed_tgt, 0);
@@ -6153,11 +6269,7 @@ static ssize_t scst_tg_tgt_rel_tgt_id_store(struct kobject *kobj,
TRACE_ENTRY();
tg_tgt = container_of(kobj, struct scst_tg_tgt, kobj);
snprintf(ch, sizeof(ch), "%.*s", min_t(int, count, sizeof(ch)-1), buf);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
res = kstrtoul(ch, 0, &rel_tgt_id);
#else
res = strict_strtoul(ch, 0, &rel_tgt_id);
#endif
if (res)
goto out;
res = -EINVAL;
@@ -6247,11 +6359,7 @@ static ssize_t scst_tg_group_id_store(struct kobject *kobj,
TRACE_ENTRY();
tg = container_of(kobj, struct scst_target_group, kobj);
snprintf(ch, sizeof(ch), "%.*s", min_t(int, count, sizeof(ch)-1), buf);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
res = kstrtoul(ch, 0, &group_id);
#else
res = strict_strtoul(ch, 0, &group_id);
#endif
if (res)
goto out;
res = -EINVAL;
@@ -6289,11 +6397,7 @@ static int scst_tg_preferred_store_work_fn(struct scst_sysfs_work_item *w)
TRACE_ENTRY();
cmd = w->buf;
tg = container_of(w->kobj, struct scst_target_group, kobj);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
res = kstrtoul(cmd, 0, &preferred);
#else
res = strict_strtoul(cmd, 0, &preferred);
#endif
if (res)
goto out;
res = -EINVAL;
@@ -6827,13 +6931,9 @@ static ssize_t scst_threads_store(struct kobject *kobj,
TRACE_ENTRY();
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
res = kstrtol(buf, 0, &newtn);
#else
res = strict_strtol(buf, 0, &newtn);
#endif
if (res != 0) {
PRINT_ERROR("strict_strtol() for %s failed: %d ", buf, res);
PRINT_ERROR("kstrtol() for %s failed: %d ", buf, res);
goto out;
}
if (newtn <= 0) {
@@ -6883,13 +6983,9 @@ static ssize_t scst_setup_id_store(struct kobject *kobj,
TRACE_ENTRY();
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
res = kstrtoul(buf, 0, &val);
#else
res = strict_strtoul(buf, 0, &val);
#endif
if (res != 0) {
PRINT_ERROR("strict_strtoul() for %s failed: %d ", buf, res);
PRINT_ERROR("kstrtoul() for %s failed: %d ", buf, res);
goto out;
}
@@ -6930,13 +7026,9 @@ static ssize_t scst_max_tasklet_cmd_store(struct kobject *kobj,
TRACE_ENTRY();
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
res = kstrtoul(buf, 0, &val);
#else
res = strict_strtoul(buf, 0, &val);
#endif
if (res != 0) {
PRINT_ERROR("strict_strtoul() for %s failed: %d ", buf, res);
PRINT_ERROR("kstrtoul() for %s failed: %d ", buf, res);
goto out;
}
@@ -7393,10 +7485,9 @@ int scst_wait_info_completion(struct scst_sysfs_user_info *info,
break;
}
} else if (rc != -ERESTARTSYS) {
res = rc;
PRINT_ERROR("wait_for_completion() failed: %d",
res);
goto out;
res = rc;
PRINT_ERROR("wait_for_completion() failed: %d", res);
goto out;
} else {
TRACE_DBG("Waiting for info %p finished with %d, "
"retrying", info, rc);
+532 -37
View File
@@ -103,25 +103,302 @@ static inline void scst_schedule_tasklet(struct scst_cmd *cmd)
return;
}
/* No locks */
static bool scst_check_blocked_dev(struct scst_cmd *cmd)
static bool scst_unmap_overlap(struct scst_cmd *cmd, int64_t lba2,
int64_t lba2_blocks)
{
bool res = false;
struct scst_data_descriptor *pd = cmd->cmd_data_descriptors;
int i;
TRACE_ENTRY();
if (pd == NULL)
goto out;
for (i = 0; pd[i].sdd_blocks != 0; i++) {
struct scst_data_descriptor *d = &pd[i];
TRACE_DBG("i %d, lba %lld, blocks %lld", i,
(long long)d->sdd_lba, (long long)d->sdd_blocks);
res = scst_lba1_inside_lba2(d->sdd_lba, lba2, lba2_blocks);
if (res)
goto out;
res = scst_lba1_inside_lba2(lba2, d->sdd_lba, d->sdd_blocks);
if (res)
goto out;
}
out:
TRACE_EXIT_RES(res);
return res;
}
static bool scst_cmd_overlap_cwr(struct scst_cmd *cwr_cmd, struct scst_cmd *cmd)
{
bool res;
TRACE_ENTRY();
TRACE_DBG("cwr_cmd %p, cmd %p (op %s, LBA valid %d, lba %lld, "
"len %lld)", cwr_cmd, cmd, scst_get_opcode_name(cmd),
(cmd->op_flags & SCST_LBA_NOT_VALID) == 0,
(long long)cmd->lba, (long long)cmd->data_len);
EXTRACHECKS_BUG_ON(cwr_cmd->cdb[0] != COMPARE_AND_WRITE);
/*
* In addition to requirements listed in "Model for uninterrupted
* sequences on LBA ranges" (SBC) VMware wants that COMPARE AND WRITE
* be atomic against RESERVEs, as well as RESERVEs be atomic against
* all COMPARE AND WRITE commands and only against them.
*/
if (cmd->op_flags & SCST_LBA_NOT_VALID) {
switch (cmd->cdb[0]) {
case RESERVE:
case RESERVE_10:
res = true;
break;
case UNMAP:
res = scst_unmap_overlap(cmd, cwr_cmd->lba,
cwr_cmd->data_len);
break;
case EXTENDED_COPY:
res = scst_cm_ec_cmd_overlap(cmd, cwr_cmd);
break;
default:
res = false;
break;
}
goto out;
}
/* If LBA valid, block_shift must be valid */
EXTRACHECKS_BUG_ON(cmd->dev->block_shift <= 0);
res = scst_lba1_inside_lba2(cwr_cmd->lba, cmd->lba,
cmd->data_len >> cmd->dev->block_shift);
if (res)
goto out;
res = scst_lba1_inside_lba2(cmd->lba, cwr_cmd->lba,
cwr_cmd->data_len >> cwr_cmd->dev->block_shift);
out:
TRACE_EXIT_RES(res);
return res;
}
static bool scst_cmd_overlap_reserve(struct scst_cmd *reserve_cmd,
struct scst_cmd *cmd)
{
bool res;
TRACE_ENTRY();
TRACE_DBG("reserve_cmd %p, cmd %p (op %s, LBA valid %d, lba %lld, "
"len %lld)", reserve_cmd, cmd, scst_get_opcode_name(cmd),
(cmd->op_flags & SCST_LBA_NOT_VALID) == 0,
(long long)cmd->lba, (long long)cmd->data_len);
EXTRACHECKS_BUG_ON((reserve_cmd->cdb[0] != RESERVE) &&
(reserve_cmd->cdb[0] != RESERVE_10));
/*
* In addition to requirements listed in "Model for uninterrupted
* sequences on LBA ranges" (SBC) VMware wants that COMPARE AND WRITE
* be atomic against RESERVEs, as well as RESERVEs be atomic against
* all COMPARE AND WRITE commands and only against them.
*/
if (cmd->cdb[0] == COMPARE_AND_WRITE)
res = true;
else
res = false;
TRACE_EXIT_RES(res);
return res;
}
static bool scst_cmd_overlap_atomic(struct scst_cmd *atomic_cmd, struct scst_cmd *cmd)
{
bool res;
TRACE_ENTRY();
TRACE_DBG("atomic_cmd %p (op %s), cmd %p (op %s)", atomic_cmd,
scst_get_opcode_name(atomic_cmd), cmd, scst_get_opcode_name(cmd));
EXTRACHECKS_BUG_ON((atomic_cmd->op_flags & SCST_SCSI_ATOMIC) == 0);
switch (atomic_cmd->cdb[0]) {
case COMPARE_AND_WRITE:
res = scst_cmd_overlap_cwr(atomic_cmd, cmd);
break;
case RESERVE:
case RESERVE_10:
res = scst_cmd_overlap_reserve(atomic_cmd, cmd);
break;
default:
res = false;
break;
}
TRACE_EXIT_RES(res);
return res;
}
static bool scst_cmd_overlap(struct scst_cmd *chk_cmd, struct scst_cmd *cmd)
{
bool res = false;
TRACE_ENTRY();
TRACE_DBG("chk_cmd %p, cmd %p", chk_cmd, cmd);
if ((chk_cmd->op_flags & SCST_SCSI_ATOMIC) != 0)
res = scst_cmd_overlap_atomic(chk_cmd, cmd);
else if ((cmd->op_flags & SCST_SCSI_ATOMIC) != 0)
res = scst_cmd_overlap_atomic(cmd, chk_cmd);
else
res = false;
TRACE_EXIT_RES(res);
return res;
}
/*
* dev_lock supposed to be held and BH disabled. Returns true if cmd blocked,
* hence stop processing it and go to the next command.
*/
static bool scst_check_scsi_atomicity(struct scst_cmd *chk_cmd)
{
bool res = false;
struct scst_device *dev = chk_cmd->dev;
struct scst_cmd *cmd;
TRACE_ENTRY();
/*
* chk_cmd isn't necessary SCSI atomic! For instance, if another SCSI
* atomic cmd is waiting blocked.
*/
TRACE_DBG("chk_cmd %p (op %s, internal %d, lba %lld, len %lld)",
chk_cmd, scst_get_opcode_name(chk_cmd), chk_cmd->internal,
(long long)chk_cmd->lba, (long long)chk_cmd->data_len);
list_for_each_entry(cmd, &dev->dev_exec_cmd_list, dev_exec_cmd_list_entry) {
if (chk_cmd == cmd)
continue;
if (scst_cmd_overlap(chk_cmd, cmd)) {
struct scst_cmd **p = cmd->scsi_atomic_blocked_cmds;
/*
* kmalloc() allocates by at least 32 bytes increments,
* hence krealloc() on 8 bytes increments, if not all
* that space is used, does nothing.
*/
p = krealloc(p, sizeof(*p) * (cmd->scsi_atomic_blocked_cmds_count + 1),
GFP_ATOMIC);
if (p == NULL)
goto out_busy_undo;
p[cmd->scsi_atomic_blocked_cmds_count] = chk_cmd;
cmd->scsi_atomic_blocked_cmds = p;
cmd->scsi_atomic_blocked_cmds_count++;
chk_cmd->scsi_atomic_blockers++;
TRACE_BLOCK("Delaying cmd %p (op %s, lba %lld, "
"len %lld, blockers %d) due to overlap with "
"cmd %p (op %s, lba %lld, len %lld, blocked "
"cmds %d)", chk_cmd, scst_get_opcode_name(chk_cmd),
(long long)chk_cmd->lba,
(long long)chk_cmd->data_len,
chk_cmd->scsi_atomic_blockers, cmd,
scst_get_opcode_name(cmd), (long long)cmd->lba,
(long long)cmd->data_len,
cmd->scsi_atomic_blocked_cmds_count);
res = true;
}
}
out:
TRACE_EXIT_RES(res);
return res;
out_busy_undo:
list_for_each_entry(cmd, &dev->dev_exec_cmd_list, dev_exec_cmd_list_entry) {
struct scst_cmd **p = cmd->scsi_atomic_blocked_cmds;
if ((p != NULL) && (p[cmd->scsi_atomic_blocked_cmds_count-1] == chk_cmd)) {
cmd->scsi_atomic_blocked_cmds_count--;
chk_cmd->scsi_atomic_blockers--;
}
}
sBUG_ON(chk_cmd->scsi_atomic_blockers != 0);
scst_set_busy(chk_cmd);
scst_set_cmd_abnormal_done_state(chk_cmd);
spin_lock_irq(&chk_cmd->cmd_threads->cmd_list_lock);
TRACE_MGMT_DBG("Adding on error chk_cmd %p back to head of active cmd "
"list", chk_cmd);
list_add(&chk_cmd->cmd_list_entry, &chk_cmd->cmd_threads->active_cmd_list);
wake_up(&chk_cmd->cmd_threads->cmd_list_waitQ);
spin_unlock_irq(&chk_cmd->cmd_threads->cmd_list_lock);
res = false;
goto out;
}
/*
* dev_lock supposed to be BH locked. Returns true if cmd blocked, hence stop
* processing it and go to the next command.
*/
bool scst_do_check_blocked_dev(struct scst_cmd *cmd)
{
bool res;
struct scst_device *dev = cmd->dev;
TRACE_ENTRY();
if (unlikely(cmd->internal)) {
/*
* The original command can already block the device and must
* hold reference to it, so internal command should always pass.
*/
sBUG_ON(dev->on_dev_cmd_count == 0);
res = false;
goto out;
/*
* We want to have fairness between just unblocked previously blocked
* SCSI atomic cmds and new cmds came after them. Otherwise, the new
* cmds can bypass the SCSI atomic cmds and make them unfairly wait
* again. So, we need to always, from the beginning, have blocked SCSI
* atomic cmds on the exec list, even if they blocked, as well
* as dev's SCSI atomic cmds counter incremented.
*/
if (likely(!cmd->on_dev_exec_list)) {
list_add_tail(&cmd->dev_exec_cmd_list_entry, &dev->dev_exec_cmd_list);
cmd->on_dev_exec_list = 1;
}
spin_lock_bh(&dev->dev_lock);
/*
* After a cmd passed SCSI atomicy check, there's no need to recheck SCSI
* atomicity for this cmd in future entrances here, because then all
* future overlapping with this cmd cmds will be blocked on it.
*/
if (unlikely(((cmd->op_flags & SCST_SCSI_ATOMIC) != 0) ||
(dev->dev_scsi_atomic_cmd_active != 0)) &&
!cmd->scsi_atomicity_checked) {
cmd->scsi_atomicity_checked = 1;
if ((cmd->op_flags & SCST_SCSI_ATOMIC) != 0) {
dev->dev_scsi_atomic_cmd_active++;
TRACE_DBG("cmd %p (dev %p), scsi atomic_cmd_active %d",
cmd, dev, dev->dev_scsi_atomic_cmd_active);
}
res = scst_check_scsi_atomicity(cmd);
if (res) {
EXTRACHECKS_BUG_ON(dev->dev_scsi_atomic_cmd_active == 0);
goto out;
}
}
dev->on_dev_cmd_count++;
cmd->dec_on_dev_needed = 1;
@@ -140,8 +417,46 @@ static bool scst_check_blocked_dev(struct scst_cmd *cmd)
cmd->dec_on_dev_needed = 0;
TRACE_DBG("New dec on_dev_count %d (cmd %p)",
dev->on_dev_cmd_count, cmd);
goto out;
}
out:
TRACE_EXIT_RES(res);
return res;
}
/*
* No locks. Returns true if cmd blocked, hence stop processing it and go to
* the next command.
*/
static bool scst_check_blocked_dev(struct scst_cmd *cmd)
{
bool res;
struct scst_device *dev = cmd->dev;
TRACE_ENTRY();
if (unlikely((cmd->op_flags & SCST_CAN_GEN_3PARTY_COMMANDS) != 0)) {
EXTRACHECKS_BUG_ON(cmd->cdb[0] != EXTENDED_COPY);
res = scst_cm_check_block_all_devs(cmd);
goto out;
}
if (unlikely(cmd->internal || cmd->bypass_blocking)) {
/*
* The original command can already block the device and must
* hold reference to it, so internal command should always pass.
*/
/* Copy Manager can send internal INQUIRYs, so don't BUG on them */
sBUG_ON((dev->on_dev_cmd_count == 0) && (cmd->cdb[0] != INQUIRY));
res = false;
goto out;
}
spin_lock_bh(&dev->dev_lock);
res = scst_do_check_blocked_dev(cmd);
spin_unlock_bh(&dev->dev_lock);
out:
@@ -149,12 +464,68 @@ out:
return res;
}
/* No locks */
static void scst_check_unblock_dev(struct scst_cmd *cmd)
/* dev_lock supposed to be held and BH disabled */
static void scst_check_unblock_scsi_atomic_cmds(struct scst_cmd *cmd)
{
int i;
TRACE_ENTRY();
EXTRACHECKS_BUG_ON(cmd->scsi_atomic_blocked_cmds_count == 0);
for (i = 0; i < cmd->scsi_atomic_blocked_cmds_count; i++) {
struct scst_cmd *acmd = cmd->scsi_atomic_blocked_cmds[i];
acmd->scsi_atomic_blockers--;
if (acmd->scsi_atomic_blockers == 0) {
TRACE_BLOCK("Unblocking blocked acmd %p (blocker "
"cmd %p)", acmd, cmd);
spin_lock_irq(&acmd->cmd_threads->cmd_list_lock);
if (acmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE)
list_add(&acmd->cmd_list_entry,
&acmd->cmd_threads->active_cmd_list);
else
list_add_tail(&acmd->cmd_list_entry,
&acmd->cmd_threads->active_cmd_list);
wake_up(&acmd->cmd_threads->cmd_list_waitQ);
spin_unlock_irq(&acmd->cmd_threads->cmd_list_lock);
}
}
kfree(cmd->scsi_atomic_blocked_cmds);
cmd->scsi_atomic_blocked_cmds = NULL;
cmd->scsi_atomic_blocked_cmds_count = 0;
TRACE_EXIT();
return;
}
/* dev_lock supposed to be BH locked */
void __scst_check_unblock_dev(struct scst_cmd *cmd)
{
struct scst_device *dev = cmd->dev;
spin_lock_bh(&dev->dev_lock);
TRACE_ENTRY();
/*
* We might be called here as part of Copy Manager's check blocking
* undo, so restore all flags in the previous state to allow
* restart of this cmd.
*/
if (likely(cmd->on_dev_exec_list)) {
list_del(&cmd->dev_exec_cmd_list_entry);
cmd->on_dev_exec_list = 0;
}
if (unlikely((cmd->op_flags & SCST_SCSI_ATOMIC) != 0)) {
if (likely(cmd->scsi_atomicity_checked)) {
dev->dev_scsi_atomic_cmd_active--;
TRACE_DBG("cmd %p, scsi atomic_cmd_active %d",
cmd, dev->dev_scsi_atomic_cmd_active);
cmd->scsi_atomicity_checked = 0;
}
}
if (likely(cmd->dec_on_dev_needed)) {
dev->on_dev_cmd_count--;
@@ -163,6 +534,9 @@ static void scst_check_unblock_dev(struct scst_cmd *cmd)
dev->on_dev_cmd_count, cmd);
}
if (unlikely(cmd->scsi_atomic_blocked_cmds != NULL))
scst_check_unblock_scsi_atomic_cmds(cmd);
if (unlikely(cmd->unblock_dev)) {
TRACE_BLOCK("cmd %p (tag %llu): unblocking dev %s", cmd,
(unsigned long long int)cmd->tag, dev->virt_name);
@@ -176,7 +550,30 @@ static void scst_check_unblock_dev(struct scst_cmd *cmd)
}
}
if (unlikely(dev->ext_blocking_pending)) {
if (dev->on_dev_cmd_count == 0) {
TRACE_MGMT_DBG("Releasing pending dev %s extended "
"blocks", dev->virt_name);
scst_ext_blocking_done(dev);
}
}
TRACE_EXIT();
return;
}
/* No locks */
void scst_check_unblock_dev(struct scst_cmd *cmd)
{
struct scst_device *dev = cmd->dev;
TRACE_ENTRY();
spin_lock_bh(&dev->dev_lock);
__scst_check_unblock_dev(cmd);
spin_unlock_bh(&dev->dev_lock);
TRACE_EXIT();
return;
}
@@ -373,6 +770,7 @@ out_redirect:
msleep(50);
} else {
unsigned long flags;
spin_lock_irqsave(&scst_init_lock, flags);
TRACE_DBG("Adding cmd %p to init cmd list", cmd);
list_add_tail(&cmd->cmd_list_entry, &scst_init_cmd_list);
@@ -1008,6 +1406,7 @@ out_check_compl:
if (unlikely(test_bit(SCST_TGT_DEV_BLACK_HOLE, &cmd->tgt_dev->tgt_dev_flags))) {
struct scst_session *sess = cmd->sess;
bool abort = false;
switch (sess->acg->acg_black_hole_type) {
case SCST_ACG_BLACK_HOLE_CMD:
case SCST_ACG_BLACK_HOLE_ALL:
@@ -1410,6 +1809,7 @@ static int scst_rdy_to_xfer(struct scst_cmd *cmd)
if (tgtt->on_hw_pending_cmd_timeout != NULL) {
struct scst_session *sess = cmd->sess;
cmd->hw_pending_start = jiffies;
cmd->cmd_hw_pending = 1;
if (!test_bit(SCST_SESS_HW_PENDING_WORK_SCHEDULED, &sess->sess_aflags)) {
@@ -1669,6 +2069,7 @@ static int scst_tgt_pre_exec(struct scst_cmd *cmd)
if (unlikely(cmd->resid_possible)) {
if (cmd->data_direction & SCST_DATA_WRITE) {
bool remainder = false;
if (cmd->data_direction & SCST_DATA_READ) {
if (cmd->write_len != cmd->out_bufflen)
remainder = true;
@@ -1706,7 +2107,17 @@ static int scst_tgt_pre_exec(struct scst_cmd *cmd)
cmd->state = SCST_CMD_STATE_EXEC_CHECK_SN;
if ((cmd->tgtt->pre_exec == NULL) || unlikely(cmd->internal))
if (unlikely(cmd->internal)) {
if (cmd->dh_data_buf_alloced && cmd->tgt_i_data_buf_alloced &&
(scst_cmd_get_data_direction(cmd) & SCST_DATA_WRITE)) {
TRACE_DBG("Internal WRITE cmd %p with DH alloced data",
cmd);
scst_copy_sg(cmd, SCST_SG_COPY_FROM_TARGET);
}
goto out_descr;
}
if (cmd->tgtt->pre_exec == NULL)
goto out_descr;
TRACE_DBG("Calling pre_exec(%p)", cmd);
@@ -1738,6 +2149,7 @@ static int scst_tgt_pre_exec(struct scst_cmd *cmd)
out_descr:
if (unlikely(cmd->op_flags & SCST_DESCRIPTORS_BASED)) {
int r = scst_parse_descriptors(cmd);
if (unlikely(r != 0))
goto out;
}
@@ -1889,7 +2301,7 @@ static int scst_report_luns_local(struct scst_cmd *cmd)
cmd->driver_status = 0;
if ((cmd->cdb[2] != 0) && (cmd->cdb[2] != 2)) {
TRACE(TRACE_MINOR, "Unsupported SELECT REPORT value %x in "
TRACE(TRACE_MINOR, "Unsupported SELECT REPORT value %#x in "
"REPORT LUNS command", cmd->cdb[2]);
scst_set_invalid_field_in_cdb(cmd, 2, 0);
goto out_compl;
@@ -1910,6 +2322,7 @@ static int scst_report_luns_local(struct scst_cmd *cmd)
rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &cmd->sess->sess_tgt_dev_list[i];
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
struct scst_tgt_dev_UA *ua;
@@ -2740,7 +3153,7 @@ out_unlock:
scst_put_buf_full(cmd, buffer);
out_done:
if (SCST_EXEC_COMPLETED == res) {
if (res == SCST_EXEC_COMPLETED) {
if (!aborted)
cmd->completed = 1;
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT,
@@ -2770,7 +3183,7 @@ int __scst_check_local_events(struct scst_cmd *cmd, bool preempt_tests_only)
TRACE_ENTRY();
if (unlikely(cmd->internal)) {
if (unlikely(cmd->internal && !cmd->internal_check_local_events)) {
if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))) {
TRACE_MGMT_DBG("ABORTED set, aborting internal "
"cmd %p", cmd);
@@ -2975,6 +3388,7 @@ static struct scst_cmd *scst_post_exec_sn(struct scst_cmd *cmd,
if (inc_expected_sn) {
bool rc = scst_inc_expected_sn(cmd);
if (!rc)
goto out;
if (make_active)
@@ -3008,7 +3422,7 @@ static int scst_do_real_exec(struct scst_cmd *cmd)
if (devt->exec) {
TRACE_DBG("Calling dev handler %s exec(%p)",
devt->name, cmd);
scst_set_cur_start(cmd);
scst_set_exec_start(cmd);
res = devt->exec(cmd);
TRACE_DBG("Dev handler %s exec() returned %d",
devt->name, res);
@@ -3034,7 +3448,7 @@ static int scst_do_real_exec(struct scst_cmd *cmd)
goto out_error;
}
scst_set_cur_start(cmd);
scst_set_exec_start(cmd);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
rc = scst_exec_req(scsi_dev, cmd->cdb, cmd->cdb_len,
@@ -3127,6 +3541,9 @@ static scst_local_exec_fn scst_local_fns[256] = {
[PERSISTENT_RESERVE_OUT] = scst_persistent_reserve_out_local,
[REPORT_LUNS] = scst_report_luns_local,
[REQUEST_SENSE] = scst_request_sense_local,
[COMPARE_AND_WRITE] = scst_cmp_wr_local,
[EXTENDED_COPY] = scst_cm_ext_copy_exec,
[RECEIVE_COPY_RESULTS] = scst_cm_rcv_copy_res_exec,
[MAINTENANCE_IN] = scst_maintenance_in,
};
@@ -3229,15 +3646,41 @@ out_done:
goto out;
}
static inline bool scst_check_alua(struct scst_cmd *cmd, int *out_res)
{
int (*alua_filter)(struct scst_cmd *cmd);
bool res = false;
alua_filter = ACCESS_ONCE(cmd->tgt_dev->alua_filter);
if (unlikely(alua_filter)) {
int ac = alua_filter(cmd);
if (ac != SCST_ALUA_CHECK_OK) {
if (ac != SCST_ALUA_CHECK_DELAYED) {
EXTRACHECKS_BUG_ON(cmd->status == 0);
scst_set_cmd_abnormal_done_state(cmd);
*out_res = SCST_CMD_STATE_RES_CONT_SAME;
}
res = true;
}
}
return res;
}
static int scst_exec_check_blocking(struct scst_cmd **active_cmd)
{
struct scst_cmd *cmd = *active_cmd;
struct scst_cmd *ref_cmd;
int res = SCST_CMD_STATE_RES_CONT_NEXT;
TRACE_ENTRY();
cmd->state = SCST_CMD_STATE_EXEC_CHECK_BLOCKING;
if (unlikely(scst_check_alua(cmd, &res)))
goto out;
if (unlikely(scst_check_blocked_dev(cmd)))
goto out;
@@ -3251,6 +3694,7 @@ static int scst_exec_check_blocking(struct scst_cmd **active_cmd)
#ifdef CONFIG_SCST_DEBUG_SN
if ((scst_random() % 120) == 7) {
int t = scst_random() % 200;
TRACE_SN("Delaying IO on %d ms", t);
msleep(t);
}
@@ -3280,9 +3724,9 @@ static int scst_exec_check_blocking(struct scst_cmd **active_cmd)
cmd->state = SCST_CMD_STATE_LOCAL_EXEC;
rc = scst_do_local_exec(cmd);
if (likely(rc == SCST_EXEC_NOT_COMPLETED))
/* Nothing to do */;
else {
if (likely(rc == SCST_EXEC_NOT_COMPLETED)) {
/* Nothing to do */
} else {
sBUG_ON(rc != SCST_EXEC_COMPLETED);
goto done;
}
@@ -3301,6 +3745,9 @@ done:
cmd->state = SCST_CMD_STATE_EXEC_CHECK_BLOCKING;
if (unlikely(scst_check_alua(cmd, &res)))
goto out;
if (unlikely(scst_check_blocked_dev(cmd)))
break;
@@ -3320,8 +3767,8 @@ done:
/* !! At this point sess, dev and tgt_dev can be already freed !! */
out:
TRACE_EXIT();
return SCST_CMD_STATE_RES_CONT_NEXT;
TRACE_EXIT_RES(res);
return res;
}
static int scst_exec_check_sn(struct scst_cmd **active_cmd)
@@ -3421,6 +3868,7 @@ static int scst_check_sense(struct scst_cmd *cmd)
} else {
int sl;
uint8_t sense[SCST_STANDARD_SENSE_LEN];
TRACE(TRACE_MGMT, "DID_RESET received for device %s, "
"triggering reset UA", dev->virt_name);
sl = scst_set_sense(sense, sizeof(sense), dev->d_sense,
@@ -3597,6 +4045,7 @@ next:
if (likely(scst_cmd_completed_good(cmd))) {
if (cmd->deferred_dif_read_check) {
int rc = scst_dif_process_read(cmd);
if (unlikely(rc != 0)) {
cmd->deferred_dif_read_check = 0;
goto again;
@@ -3709,6 +4158,7 @@ static int scst_mode_select_checks(struct scst_cmd *cmd)
if (likely(scsi_status_is_good(cmd->status))) {
int atomic = scst_cmd_atomic(cmd);
if (unlikely((cmd->cdb[0] == MODE_SELECT) ||
(cmd->cdb[0] == MODE_SELECT_10) ||
(cmd->cdb[0] == LOG_SELECT))) {
@@ -3762,6 +4212,7 @@ static int scst_mode_select_checks(struct scst_cmd *cmd)
SCST_SENSE_ASC_VALID,
0, 0x2F, 0))) {
int atomic = scst_cmd_atomic(cmd);
if (atomic) {
TRACE_DBG("Possible parameters changed UA %x: "
"thread context required", cmd->sense[12]);
@@ -3871,6 +4322,7 @@ static int scst_dev_done(struct scst_cmd *cmd)
if (cmd->inc_expected_sn_on_done && cmd->sent_for_exec && cmd->sn_set) {
bool rc = scst_inc_expected_sn(cmd);
if (rc)
scst_make_deferred_commands_active(cmd->cur_order_data);
}
@@ -4035,6 +4487,7 @@ static int scst_xmit_response(struct scst_cmd *cmd)
(cmd->data_direction & SCST_DATA_READ)) {
int i, sg_cnt;
struct scatterlist *sg, *sgi;
if (cmd->tgt_i_sg != NULL) {
sg = cmd->tgt_i_sg;
sg_cnt = cmd->tgt_i_sg_cnt;
@@ -4059,6 +4512,7 @@ static int scst_xmit_response(struct scst_cmd *cmd)
if (tgtt->on_hw_pending_cmd_timeout != NULL) {
struct scst_session *sess = cmd->sess;
cmd->hw_pending_start = jiffies;
cmd->cmd_hw_pending = 1;
if (!test_bit(SCST_SESS_HW_PENDING_WORK_SCHEDULED, &sess->sess_aflags)) {
@@ -4216,6 +4670,12 @@ static int scst_finish_cmd(struct scst_cmd *cmd)
spin_unlock_irq(&sess->sess_list_lock);
if (unlikely(cmd->cmd_on_global_stpg_list)) {
TRACE_DBG("Unlisting being freed STPG cmd %p", cmd);
EXTRACHECKS_BUG_ON(cmd->cmd_global_stpg_blocked);
scst_stpg_del_unblock_next(cmd);
}
if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)))
scst_finish_cmd_mgmt(cmd);
@@ -4313,6 +4773,7 @@ again:
order_data->cur_sn_slot = order_data->sn_slots;
if (unlikely(atomic_read(order_data->cur_sn_slot) != 0)) {
static int q;
if (q++ < 10)
PRINT_WARNING("Not enough SN slots "
"(dev %s)", cmd->dev->virt_name);
@@ -4487,6 +4948,7 @@ static int scst_translate_lun(struct scst_cmd *cmd)
"unexisting LU (initiator %s, target %s)?",
(unsigned long long int)cmd->lun,
cmd->sess->initiator_name, cmd->tgt->tgt_name);
scst_event_queue_lun_not_found(cmd);
}
scst_put(cmd->cpu_cmd_counter);
}
@@ -4508,7 +4970,6 @@ static int scst_translate_lun(struct scst_cmd *cmd)
*/
static int __scst_init_cmd(struct scst_cmd *cmd)
{
bool (*alua_filter)(struct scst_cmd *cmd);
int res = 0;
TRACE_ENTRY();
@@ -4549,12 +5010,6 @@ static int __scst_init_cmd(struct scst_cmd *cmd)
if (unlikely(failure))
goto out_busy;
alua_filter = ACCESS_ONCE(cmd->tgt_dev->alua_filter);
if (unlikely(alua_filter && !alua_filter(cmd))) {
scst_set_cmd_abnormal_done_state(cmd);
goto out;
}
/*
* SCST_IMPLICIT_HQ for unknown commands not implemented for
* case when set_sn_on_restart_cmd not set, because custom parse
@@ -4571,6 +5026,7 @@ static int __scst_init_cmd(struct scst_cmd *cmd)
else {
struct scst_order_data *order_data = cmd->cur_order_data;
unsigned long flags;
spin_lock_irqsave(&order_data->init_done_lock, flags);
scst_cmd_set_sn(cmd);
spin_unlock_irqrestore(&order_data->init_done_lock, flags);
@@ -4615,6 +5071,7 @@ restart:
list_for_each_entry(cmd, &scst_init_cmd_list, cmd_list_entry) {
int rc;
if (susp && !test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))
continue;
if (!test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)) {
@@ -5381,6 +5838,9 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
TRACE_ENTRY();
/* Fantom EC commands must not leak here */
sBUG_ON((cmd->cdb[0] == EXTENDED_COPY) && cmd->internal);
/*
* Help Coverity recognize that mcmd != NULL if
* call_dev_task_mgmt_fn_received == true.
@@ -5430,6 +5890,9 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
*/
smp_mb__after_set_bit();
if (cmd->cdb[0] == EXTENDED_COPY)
scst_cm_abort_ec_cmd(cmd);
if (cmd->tgt_dev == NULL) {
spin_lock_irqsave(&scst_init_lock, flags);
scst_init_poll_cnt++;
@@ -5488,6 +5951,7 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd,
if (mstb->done_counted || mstb->finish_counted) {
unsigned long t;
char state_name[32];
if (mcmd->fn != SCST_PR_ABORT_ALL)
t = TRACE_MGMT;
else
@@ -5575,6 +6039,7 @@ static int scst_set_mcmd_next_state(struct scst_mgmt_cmd *mcmd)
default:
{
char fn_name[16], state_name[32];
PRINT_CRIT_ERROR("Wrong mcmd %p state %s (fn %s, "
"cmd_finish_wait_count %d, cmd_done_wait_count %d)",
mcmd, scst_get_mcmd_state_name(state_name,
@@ -5596,11 +6061,14 @@ static int scst_set_mcmd_next_state(struct scst_mgmt_cmd *mcmd)
/* IRQs supposed to be disabled */
static bool __scst_check_unblock_aborted_cmd(struct scst_cmd *cmd,
struct list_head *list_entry)
struct list_head *list_entry, bool blocked)
{
bool res;
if (test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)) {
list_del(list_entry);
if (blocked)
cmd->cmd_global_stpg_blocked = 0;
spin_lock(&cmd->cmd_threads->cmd_list_lock);
list_add_tail(&cmd->cmd_list_entry,
&cmd->cmd_threads->active_cmd_list);
@@ -5643,7 +6111,7 @@ void scst_unblock_aborted_cmds(const struct scst_tgt *tgt,
continue;
if (__scst_check_unblock_aborted_cmd(cmd,
&cmd->blocked_cmd_list_entry)) {
&cmd->blocked_cmd_list_entry, true)) {
TRACE_MGMT_DBG("Unblock aborted blocked cmd %p", cmd);
}
}
@@ -5654,6 +6122,7 @@ void scst_unblock_aborted_cmds(const struct scst_tgt *tgt,
list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list,
dev_tgt_dev_list_entry) {
struct scst_order_data *order_data = tgt_dev->curr_order_data;
spin_lock(&order_data->sn_lock);
list_for_each_entry_safe(cmd, tcmd,
&order_data->deferred_cmd_list,
@@ -5665,7 +6134,7 @@ void scst_unblock_aborted_cmds(const struct scst_tgt *tgt,
continue;
if (__scst_check_unblock_aborted_cmd(cmd,
&cmd->deferred_cmd_list_entry)) {
&cmd->deferred_cmd_list_entry, false)) {
TRACE_MGMT_DBG("Unblocked aborted SN "
"cmd %p (sn %u)", cmd, cmd->sn);
order_data->def_cmd_count--;
@@ -5872,8 +6341,10 @@ static int scst_clear_task_set(struct scst_mgmt_cmd *mcmd)
return res;
}
/* Returns 0 if the command processing should be continued,
* >0, if it should be requeued, <0 otherwise */
/*
* Returns 0 if the command processing should be continued,
* >0, if it should be requeued, <0 otherwise.
*/
static int scst_mgmt_cmd_init(struct scst_mgmt_cmd *mcmd)
{
int res = 0, rc, t;
@@ -5966,6 +6437,8 @@ static int scst_mgmt_cmd_init(struct scst_mgmt_cmd *mcmd)
}
out:
scst_event_queue_tm_fn_received(mcmd);
TRACE_EXIT_RES(res);
return res;
}
@@ -6152,6 +6625,7 @@ static void scst_do_nexus_loss_sess(struct scst_mgmt_cmd *mcmd)
rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i];
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
scst_nexus_loss(tgt_dev,
@@ -6186,6 +6660,7 @@ static int scst_abort_all_nexus_loss_sess(struct scst_mgmt_cmd *mcmd,
rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i];
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
__scst_abort_task_set(mcmd, tgt_dev);
@@ -6220,6 +6695,7 @@ static void scst_do_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd)
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
scst_nexus_loss(tgt_dev, true);
@@ -6257,6 +6733,7 @@ static int scst_abort_all_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd,
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
__scst_abort_task_set(mcmd, tgt_dev);
@@ -6470,6 +6947,7 @@ static int scst_mgmt_affected_cmds_done(struct scst_mgmt_cmd *mcmd)
rcu_read_lock();
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &sess->sess_tgt_dev_list[i];
list_for_each_entry_rcu(tgt_dev, head, sess_tgt_dev_list_entry) {
scst_call_dev_task_mgmt_fn_done(mcmd, tgt_dev);
}
@@ -6489,6 +6967,7 @@ static int scst_mgmt_affected_cmds_done(struct scst_mgmt_cmd *mcmd)
for (i = 0; i < SESS_TGT_DEV_LIST_HASH_SIZE; i++) {
struct list_head *head = &s->sess_tgt_dev_list[i];
struct scst_tgt_dev *tgt_dev;
list_for_each_entry_rcu(tgt_dev, head,
sess_tgt_dev_list_entry) {
if (mcmd->sess == tgt_dev->sess)
@@ -6511,6 +6990,16 @@ static int scst_mgmt_affected_cmds_done(struct scst_mgmt_cmd *mcmd)
tgt_done:
scst_call_task_mgmt_affected_cmds_done(mcmd);
switch (mcmd->fn) {
case SCST_LUN_RESET:
case SCST_TARGET_RESET:
case SCST_NEXUS_LOSS_SESS:
case SCST_NEXUS_LOSS:
case SCST_UNREG_SESS_TM:
scst_cm_free_pending_list_ids(sess);
break;
}
res = scst_set_mcmd_next_state(mcmd);
TRACE_EXIT_RES(res);
@@ -6631,6 +7120,7 @@ static int scst_process_mgmt_cmd(struct scst_mgmt_cmd *mcmd)
default:
{
char fn_name[16], state_name[32];
PRINT_CRIT_ERROR("Wrong mcmd %p state %s (fn %s, "
"cmd_finish_wait_count %d, cmd_done_wait_count "
"%d)", mcmd, scst_get_mcmd_state_name(state_name,
@@ -6674,6 +7164,7 @@ int scst_tm_thread(void *arg)
while (!list_empty(&scst_active_mgmt_cmd_list)) {
int rc;
struct scst_mgmt_cmd *mcmd;
mcmd = list_first_entry(&scst_active_mgmt_cmd_list,
typeof(*mcmd), mgmt_cmd_list_entry);
TRACE_MGMT_DBG("Deleting mgmt cmd %p from active cmd "
@@ -7107,6 +7598,9 @@ bool scst_initiator_has_luns(struct scst_tgt *tgt, const char *initiator_name)
res = !list_empty(&acg->acg_dev_list);
if (!res)
scst_event_queue_negative_luns_inquiry(tgt, initiator_name);
mutex_unlock(&scst_mutex);
TRACE_EXIT_RES(res);
@@ -7669,6 +8163,7 @@ struct scst_cmd *scst_find_cmd_by_tag(struct scst_session *sess,
{
unsigned long flags;
struct scst_cmd *cmd;
spin_lock_irqsave(&sess->sess_list_lock, flags);
cmd = __scst_find_cmd_by_tag(sess, tag, false);
spin_unlock_irqrestore(&sess->sess_list_lock, flags);
+653 -67
View File
@@ -16,13 +16,18 @@
*/
#include <linux/moduleparam.h>
#include <linux/delay.h>
#include <linux/kmod.h>
#include <asm/unaligned.h>
#ifdef INSIDE_KERNEL_TREE
#include <scst/scst.h>
#include <scst/scst_event.h>
#else
#include "scst.h"
#include "scst_event.h"
#endif
#include "scst_priv.h"
#include "scst_pres.h"
struct alua_state_and_name {
enum scst_tg_state s;
@@ -65,6 +70,7 @@ const char *scst_alua_state_name(enum scst_tg_state s)
return NULL;
}
EXPORT_SYMBOL(scst_alua_state_name);
enum scst_tg_state scst_alua_name_to_state(const char *n)
{
@@ -127,8 +133,8 @@ static struct scst_tg_tgt *__lookup_dg_tgt(struct scst_dev_group *dg,
}
/* Look up a target group by name in the given device group. */
static struct scst_target_group *
__lookup_tg_by_name(struct scst_dev_group *dg, const char *name)
static struct scst_target_group *__lookup_tg_by_name(struct scst_dev_group *dg,
const char *name)
{
struct scst_target_group *tg;
@@ -141,9 +147,24 @@ __lookup_tg_by_name(struct scst_dev_group *dg, const char *name)
return NULL;
}
/* Look up a target group by group ID. */
static struct scst_target_group *__lookup_tg_by_group_id(struct scst_dev_group *dg,
uint16_t group_id)
{
struct scst_target_group *tg;
lockdep_assert_held(&scst_mutex);
list_for_each_entry(tg, &dg->tg_list, entry)
if (tg->group_id == group_id)
return tg;
return NULL;
}
/* Look up a target group by target port. */
static struct scst_target_group *
__lookup_tg_by_tgt(struct scst_dev_group *dg, const struct scst_tgt *tgt)
static struct scst_target_group *__lookup_tg_by_tgt(struct scst_dev_group *dg,
const struct scst_tgt *tgt)
{
struct scst_target_group *tg;
struct scst_tg_tgt *tg_tgt;
@@ -253,11 +274,14 @@ static struct kobj_type scst_tg_tgt_ktype = {
};
/*
* Whether or not to accept a command in the ALUA unavailable and transitioning
* states.
* Whether or not to accept a command in the ALUA standby state.
*/
static bool scst_tg_accept(struct scst_cmd *cmd)
static int scst_tg_accept_standby(struct scst_cmd *cmd)
{
int res;
TRACE_ENTRY();
switch (cmd->cdb[0]) {
case TEST_UNIT_READY:
case GET_EVENT_STATUS_NOTIFICATION:
@@ -273,41 +297,6 @@ static bool scst_tg_accept(struct scst_cmd *cmd)
case RESERVE_10:
case READ_BUFFER:
case WRITE_BUFFER:
return true;
case SERVICE_ACTION_IN_16:
switch (cmd->cdb[1] & 0x1f) {
case SAI_READ_CAPACITY_16:
return true;
}
break;
case MAINTENANCE_IN:
switch (cmd->cdb[1] & 0x1f) {
case MI_REPORT_TARGET_PGS:
return true;
}
break;
case MAINTENANCE_OUT:
switch (cmd->cdb[1] & 0x1f) {
case MO_SET_TARGET_PGS:
return true;
}
break;
}
return false;
}
/*
* Whether or not to accept a command in the ALUA standby state.
*/
static bool scst_tg_accept_standby(struct scst_cmd *cmd)
{
bool process_cmd = scst_tg_accept(cmd);
if (process_cmd)
return process_cmd;
switch (cmd->cdb[0]) {
case MODE_SELECT:
case MODE_SELECT_10:
case LOG_SELECT:
@@ -316,43 +305,187 @@ static bool scst_tg_accept_standby(struct scst_cmd *cmd)
case SEND_DIAGNOSTIC:
case PERSISTENT_RESERVE_IN:
case PERSISTENT_RESERVE_OUT:
return true;
res = SCST_ALUA_CHECK_OK;
goto out;
case SERVICE_ACTION_IN_16:
switch (cmd->cdb[1] & 0x1f) {
case SAI_READ_CAPACITY_16:
res = SCST_ALUA_CHECK_OK;
goto out;
}
break;
case MAINTENANCE_IN:
switch (cmd->cdb[1] & 0x1f) {
case MI_REPORT_TARGET_PGS:
res = SCST_ALUA_CHECK_OK;
goto out;
}
break;
case MAINTENANCE_OUT:
switch (cmd->cdb[1] & 0x1f) {
case MO_SET_TARGET_PGS:
res = SCST_ALUA_CHECK_OK;
goto out;
}
break;
}
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_tp_standby));
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_alua_standby));
res = SCST_ALUA_CHECK_ERROR;
return false;
out:
TRACE_EXIT_RES(res);
return res;
}
/*
* Whether or not to accept a command in the ALUA unavailable state.
*/
static bool scst_tg_accept_unav(struct scst_cmd *cmd)
static int scst_tg_accept_unav(struct scst_cmd *cmd)
{
bool process_cmd = scst_tg_accept(cmd);
int res;
if (!process_cmd)
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_tp_unav));
TRACE_ENTRY();
return process_cmd;
switch (cmd->cdb[0]) {
case TEST_UNIT_READY:
case GET_EVENT_STATUS_NOTIFICATION:
case INQUIRY:
case MODE_SENSE:
case MODE_SENSE_10:
case READ_CAPACITY:
case REPORT_LUNS:
case REQUEST_SENSE:
case RELEASE:
case RELEASE_10:
case RESERVE:
case RESERVE_10:
case READ_BUFFER:
case WRITE_BUFFER:
res = SCST_ALUA_CHECK_OK;
goto out;
case SERVICE_ACTION_IN_16:
switch (cmd->cdb[1] & 0x1f) {
case SAI_READ_CAPACITY_16:
res = SCST_ALUA_CHECK_OK;
goto out;
}
break;
case MAINTENANCE_IN:
switch (cmd->cdb[1] & 0x1f) {
case MI_REPORT_TARGET_PGS:
res = SCST_ALUA_CHECK_OK;
goto out;
}
break;
case MAINTENANCE_OUT:
switch (cmd->cdb[1] & 0x1f) {
case MO_SET_TARGET_PGS:
res = SCST_ALUA_CHECK_OK;
goto out;
}
break;
}
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_alua_unav));
res = SCST_ALUA_CHECK_ERROR;
out:
TRACE_EXIT_RES(res);
return res;
}
struct scst_alua_retry {
struct scst_cmd *alua_retry_cmd;
struct delayed_work alua_retry_work;
};
static void scst_alua_transitioning_work_fn(struct delayed_work *work)
{
struct scst_alua_retry *retry = container_of(work, struct scst_alua_retry,
alua_retry_work);
struct scst_cmd *cmd = retry->alua_retry_cmd;
TRACE_ENTRY();
TRACE_DBG("Retrying transitioning cmd %p", cmd);
spin_lock_irq(&cmd->cmd_threads->cmd_list_lock);
list_add(&cmd->cmd_list_entry,
&cmd->cmd_threads->active_cmd_list);
wake_up(&cmd->cmd_threads->cmd_list_waitQ);
spin_unlock_irq(&cmd->cmd_threads->cmd_list_lock);
kfree(retry);
TRACE_EXIT();
return;
}
/*
* Whether or not to accept a command in the ALUA transitioning state.
*/
static bool scst_tg_accept_transitioning(struct scst_cmd *cmd)
static int scst_tg_accept_transitioning(struct scst_cmd *cmd)
{
bool process_cmd = scst_tg_accept(cmd);
int res;
if (!process_cmd)
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_tp_transitioning));
TRACE_ENTRY();
return process_cmd;
switch (cmd->cdb[0]) {
case INQUIRY:
case READ_CAPACITY:
case REPORT_LUNS:
case REQUEST_SENSE:
case READ_BUFFER:
case WRITE_BUFFER:
res = SCST_ALUA_CHECK_OK;
goto out;
case SERVICE_ACTION_IN_16:
switch (cmd->cdb[1] & 0x1f) {
case SAI_READ_CAPACITY_16:
res = SCST_ALUA_CHECK_OK;
goto out;
}
break;
}
if (cmd->already_transitioning)
TRACE_DBG("cmd %p already transitioned checked, failing", cmd);
else {
struct scst_alua_retry *retry;
TRACE_DBG("ALUA transitioning: delaying cmd %p", cmd);
retry = kzalloc(sizeof(*retry), GFP_KERNEL);
if (retry == NULL) {
TRACE_DBG("Unable to allocate ALUA retry "
"struct, failing cmd %p", cmd);
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_alua_transitioning));
res = SCST_ALUA_CHECK_ERROR;
goto out;
}
/* No get is needed, because cmd is sync here */
retry->alua_retry_cmd = cmd;
INIT_DELAYED_WORK(&retry->alua_retry_work,
(void (*)(struct work_struct *))scst_alua_transitioning_work_fn);
cmd->already_transitioning = 1;
schedule_delayed_work(&retry->alua_retry_work, HZ/2);
res = SCST_ALUA_CHECK_DELAYED;
goto out;
}
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_alua_transitioning));
res = SCST_ALUA_CHECK_ERROR;
out:
TRACE_EXIT_RES(res);
return res;
}
static bool (*scst_alua_filter[])(struct scst_cmd *cmd) = {
static int (*scst_alua_filter[])(struct scst_cmd *cmd) = {
[SCST_TG_STATE_OPTIMIZED] = NULL,
[SCST_TG_STATE_NONOPTIMIZED] = NULL,
[SCST_TG_STATE_STANDBY] = scst_tg_accept_standby,
@@ -424,7 +557,9 @@ static void scst_tg_change_tgt_dev_state(struct scst_tgt_dev *tgt_dev,
{
lockdep_assert_held(&scst_dg_mutex);
TRACE_MGMT_DBG("ALUA state of tgt_dev %p has changed", tgt_dev);
TRACE_MGMT_DBG("ALUA state of tgt_dev %p has changed (gen_ua %d)",
tgt_dev, gen_ua);
scst_update_tgt_dev_alua_filter(tgt_dev, state);
if (gen_ua)
scst_gen_aen_or_ua(tgt_dev,
@@ -724,20 +859,96 @@ out:
return res;
}
static void scst_event_stpg_notify_fn(struct scst_event *event,
void *priv, int status)
{
struct scst_dev_group *dg;
struct scst_cmd *cmd = (struct scst_cmd *)priv;
struct scst_event_stpg_payload *p =
(struct scst_event_stpg_payload *)event->payload;
struct scst_event_stpg_descr *d;
struct scst_dg_dev *dgd;
int i;
TRACE_ENTRY();
PRINT_INFO("Notification for event %u (id %d) received "
"with status %d (priv %p)", event->event_code,
event->event_id, status, priv);
mutex_lock(&scst_mutex);
mutex_lock(&scst_dg_mutex);
dg = __lookup_dg_by_dev(cmd->dev);
if (!dg) {
PRINT_ERROR("STPG: unable to find DG for device %s",
cmd->dev->virt_name);
goto out_fail;
}
list_for_each_entry(dgd, &dg->dev_list, entry) {
if (dgd->dev->stpg_ext_blocked) {
TRACE_DBG("STPG: ext unblocking dev %s",
dgd->dev->virt_name);
scst_ext_unblock_dev(dgd->dev, true);
dgd->dev->stpg_ext_blocked = 0;
}
}
kfree(dg->stpg_transport_id);
dg->stpg_transport_id = NULL;
if (status != 0) {
PRINT_ERROR("on_stpg script for device group %s failed with status %d",
dg->name, status);
goto out_fail;
}
for (i = 0, d = &p->stpg_descriptors[0]; i < p->stpg_descriptors_cnt; i++, d++) {
struct scst_target_group *tg = __lookup_tg_by_group_id(dg, d->group_id);
if (!tg) {
PRINT_ERROR("STPG: unable to find TG %d", d->group_id);
goto out_fail;
} else if (tg->state == scst_alua_name_to_state(d->prev_state)) {
PRINT_ERROR("on_stpg script did not change ALUA state"
" for device group %s / target group %s",
dg->name, tg->name);
goto out_fail;
}
}
out_unlock:
mutex_unlock(&scst_dg_mutex);
mutex_unlock(&scst_mutex);
scst_stpg_del_unblock_next(cmd);
cmd->completed = 1;
cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_THREAD);
TRACE_EXIT();
return;
out_fail:
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_set_target_pgs_failed));
goto out_unlock;
}
/*
* Update the ALUA filter of those LUNs (tgt_dev) whose target port is a member
* of target group @tg and that export a device that is a member of the device
* group @tg->dg.
*/
static void __scst_tg_set_state(struct scst_target_group *tg,
enum scst_tg_state state,
struct scst_tgt *no_ua_tgt)
enum scst_tg_state state)
{
struct scst_dg_dev *dg_dev;
struct scst_device *dev;
struct scst_tgt_dev *tgt_dev;
struct scst_tg_tgt *tg_tgt;
struct scst_tgt *tgt;
enum scst_tg_state old_state = tg->state;
sBUG_ON(state >= ARRAY_SIZE(scst_alua_filter));
lockdep_assert_held(&scst_dg_mutex);
@@ -749,17 +960,26 @@ static void __scst_tg_set_state(struct scst_target_group *tg,
list_for_each_entry(dg_dev, &tg->dg->dev_list, entry) {
dev = dg_dev->dev;
if (dev->handler->on_alua_state_change_start != NULL)
dev->handler->on_alua_state_change_start(dev, old_state, state);
list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list,
dev_tgt_dev_list_entry) {
tgt = tgt_dev->sess->tgt;
list_for_each_entry(tg_tgt, &tg->tgt_list, entry) {
if (tg_tgt->tgt == tgt) {
bool gen_ua = (state != SCST_TG_STATE_TRANSITIONING);
if ((tg->dg->stpg_rel_tgt_id == tgt_dev->sess->tgt->rel_tgt_id) &&
tid_equal(tg->dg->stpg_transport_id, tgt_dev->sess->transport_id))
gen_ua = false;
scst_tg_change_tgt_dev_state(tgt_dev,
state, tgt != no_ua_tgt);
state, gen_ua);
break;
}
}
}
if (dev->handler->on_alua_state_change_finish != NULL)
dev->handler->on_alua_state_change_finish(dev, old_state, state);
}
scst_check_alua_invariant();
@@ -780,7 +1000,7 @@ int scst_tg_set_state(struct scst_target_group *tg, enum scst_tg_state state)
if (res)
goto out;
__scst_tg_set_state(tg, state, NULL);
__scst_tg_set_state(tg, state);
mutex_unlock(&scst_dg_mutex);
out:
@@ -945,9 +1165,16 @@ out_free:
goto out;
}
/* scst_dg_mutex supposed to be locked */
static void __scst_dg_dev_remove(struct scst_dev_group *dg,
struct scst_dg_dev *dgdev)
{
if (dgdev->dev->stpg_ext_blocked) {
TRACE_DBG("DG %s remove: unblocking STPG ext blocked "
"dev %s", dg->name, dgdev->dev->virt_name);
scst_ext_unblock_dev(dgdev->dev, true);
dgdev->dev->stpg_ext_blocked = 0;
}
list_del(&dgdev->entry);
scst_dg_dev_sysfs_del(dg, dgdev);
scst_reset_dev_alua_filter(dgdev->dev);
@@ -1080,7 +1307,7 @@ static void __scst_dg_remove(struct scst_dev_group *dg)
list_del(&dg->entry);
scst_dg_sysfs_del(dg);
list_for_each_entry(tg, &dg->tg_list, entry)
__scst_tg_set_state(tg, SCST_TG_STATE_OPTIMIZED, NULL);
__scst_tg_set_state(tg, SCST_TG_STATE_OPTIMIZED);
while (!list_empty(&dg->dev_list)) {
dgdev = list_first_entry(&dg->dev_list, struct scst_dg_dev,
entry);
@@ -1201,10 +1428,10 @@ out_unlock:
EXPORT_SYMBOL_GPL(scst_lookup_tg_id);
/**
* scst_impl_alua_configured() - Whether implicit ALUA has been configured.
* scst_alua_configured() - Whether implicit ALUA has been configured.
* @dev: Pointer to the SCST device to verify.
*/
bool scst_impl_alua_configured(struct scst_device *dev)
bool scst_alua_configured(struct scst_device *dev)
{
struct scst_dev_group *dg;
@@ -1214,7 +1441,7 @@ bool scst_impl_alua_configured(struct scst_device *dev)
return dg != NULL;
}
EXPORT_SYMBOL_GPL(scst_impl_alua_configured);
EXPORT_SYMBOL_GPL(scst_alua_configured);
/**
* scst_tg_get_group_info() - Build REPORT TARGET GROUPS response.
@@ -1329,3 +1556,362 @@ out:
return res;
}
EXPORT_SYMBOL_GPL(scst_tg_get_group_info);
struct scst_stpg_wait {
atomic_t stpg_wait_left;
int status;
struct scst_dev_group *dg;
struct scst_event_entry *event_entry;
};
/* No locks */
static void scst_stpg_check_blocking_done(struct scst_stpg_wait *wait)
{
TRACE_ENTRY();
TRACE_DBG("wait %p, left %d", wait, atomic_read(&wait->stpg_wait_left));
if (atomic_dec_and_test(&wait->stpg_wait_left)) {
if (wait->status == 0)
scst_event_queue(SCST_EVENT_STPG_USER_INVOKE,
SCST_EVENT_SCST_CORE_ISSUER, wait->event_entry);
else {
wait->event_entry->event_notify_fn(&wait->event_entry->event,
wait->event_entry->notify_fn_priv, wait->status);
}
kfree(wait);
}
TRACE_EXIT();
return;
}
/* No locks */
static void scst_stpg_ext_blocking_done(struct scst_device *dev,
uint8_t *data, int len)
{
sBUG_ON(len != sizeof(data));
scst_stpg_check_blocking_done(*((struct scst_stpg_wait **)data));
}
/**
* scst_tg_set_group_info - SET TARGET PORT GROUPS implementation.
*
* Returns >=0 upon success or negative error code otherwise, for instance,
* if either an invalid group ID has been specified or the group ID
* of a target group with one, or more non-local target ports has been
* specified. In the error case the cmd has its sense set.
*
* In case of returned 0 the command completed asynchronously, i.e. upon
* return might be already dead!!
*/
int scst_tg_set_group_info(struct scst_cmd *cmd)
{
struct scst_device *dev = cmd->dev;
uint8_t *buf;
int len;
int i, j, res = 1, tpg_desc_count, valid_desc_count;
struct scst_dev_group *dg;
struct osi {
uint16_t group_id;
struct scst_target_group *tg;
enum scst_tg_state prev_state;
enum scst_tg_state new_state;
} *osi = NULL;
int event_entry_len, payload_len;
struct scst_event_entry *event_entry;
struct scst_event *event;
struct scst_event_stpg_payload *payload;
struct scst_event_stpg_descr *descr;
TRACE_ENTRY();
len = scst_get_buf_full(cmd, &buf);
if (len < 0) {
PRINT_ERROR("scst_get_buf_full() failed: %d", len);
res = len;
if (len == -ENOMEM)
scst_set_busy(cmd);
else
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error));
goto out;
}
/*
* From SPC-4: "A parameter list length of zero specifies that no data
* shall be transferred, and that no change shall be made in the
* target port asymmetric access state of any target port groups or
* target ports".
*/
if (len == 0)
goto out_put;
tpg_desc_count = (len - 4) / 4;
/* Check for some reasonable limit */
if (tpg_desc_count > 64) {
PRINT_ERROR("Too many STPG descriptors (%d) for dev %s",
tpg_desc_count, dev->virt_name);
res = -EINVAL;
scst_set_invalid_field_in_cdb(cmd, 6, 0);
goto out_put;
}
TRACE_DBG("tpg_desc_count %d", tpg_desc_count);
osi = kcalloc(tpg_desc_count, sizeof(*osi), GFP_KERNEL);
if (!osi) {
res = -ENOMEM;
scst_set_busy(cmd);
goto out_put;
}
res = mutex_lock_interruptible(&scst_mutex);
if (res) {
PRINT_INFO("mutex_lock_interruptible() returned %d, finishing "
"cmd %p", res, cmd);
scst_set_busy(cmd);
goto out_put;
}
res = mutex_lock_interruptible(&scst_dg_mutex);
if (res) {
PRINT_INFO("mutex_lock_interruptible() returned %d, finishing "
"cmd %p", res, cmd);
scst_set_busy(cmd);
goto out_unlock_sm_fail;
}
dg = __lookup_dg_by_dev(dev);
if (!dg) {
res = -EINVAL;
goto out_unlock_fail;
}
TRACE_DBG("dg %s (%p) found, dev %s", dg->name, dg, dev->virt_name);
for (i = 4, j = 0; i + 4 <= len; i += 4, j++) {
#ifndef __CHECKER__
/*
* Hide the statement below for smatch because otherwise it
* triggers a false positive.
*/
WARN_ON_ONCE(j >= tpg_desc_count);
#endif
osi[j].new_state = buf[i] & 0x1f;
switch (osi[j].new_state) {
case SCST_TG_STATE_OPTIMIZED:
case SCST_TG_STATE_NONOPTIMIZED:
case SCST_TG_STATE_STANDBY:
case SCST_TG_STATE_UNAVAILABLE:
case SCST_TG_STATE_OFFLINE:
break;
default:
TRACE_MGMT_DBG("Incorrect new state %d", osi[j].new_state);
res = -EINVAL;
goto out_unlock_fail;
}
osi[j].group_id = get_unaligned_be16(&buf[i + 2]);
if (!osi[j].group_id) {
TRACE_MGMT_DBG("Invalid group_id %d", osi[j].group_id);
res = -EINVAL;
goto out_unlock_fail;
}
osi[j].tg = __lookup_tg_by_group_id(dg, osi[j].group_id);
if (!osi[j].tg) {
TRACE_MGMT_DBG("No TG for group_id %d", osi[j].group_id);
res = -ESRCH;
goto out_unlock_fail;
}
if (osi[j].tg->state == SCST_TG_STATE_TRANSITIONING) {
TRACE_MGMT_DBG("TG %p is transitioning", osi[j].tg);
res = -EBUSY;
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_alua_transitioning));
/* second sense will not override the set one */
goto out_unlock_fail;
}
osi[j].prev_state = osi[j].tg->state;
TRACE_DBG("j %d, group_id %u, tg %s (%p), state %d", j, osi[j].group_id,
osi[j].tg->name, osi[j].tg, osi[j].tg->state);
}
mutex_unlock(&scst_dg_mutex);
mutex_unlock(&scst_mutex);
scst_put_buf_full(cmd, buf);
payload_len = sizeof(*payload) + sizeof(*descr) * tpg_desc_count;
event_entry_len = sizeof(*event_entry) + payload_len;
event_entry = kzalloc(event_entry_len, GFP_KERNEL);
if (event_entry == NULL) {
PRINT_ERROR("Unable to allocate event (size %d)", event_entry_len);
res = -ENOMEM;
scst_set_busy(cmd);
goto out_free;
}
TRACE_MEM("event_entry %p (len %d) allocated", event_entry,
event_entry_len);
event = &event_entry->event;
event->payload_len = payload_len;
payload = (struct scst_event_stpg_payload *)event->payload;
payload->stpg_cmd_tag = cmd->tag;
res = 1;
if (strlen(dev->virt_name) >= sizeof(payload->device_name)) {
PRINT_ERROR("Device name %s too long", dev->virt_name);
goto out_too_long;
}
strlcpy(payload->device_name, dev->virt_name, sizeof(payload->device_name));
valid_desc_count = 0;
for (j = 0, descr = &payload->stpg_descriptors[0]; j < tpg_desc_count; j++) {
if (osi[j].prev_state == osi[j].new_state)
continue;
if (strlen(scst_alua_state_name(osi[j].prev_state)) >= sizeof(descr->prev_state)) {
PRINT_ERROR("prev state too long (%d)", osi[j].prev_state);
goto out_too_long;
}
strlcpy(descr->prev_state, scst_alua_state_name(osi[j].prev_state),
sizeof(descr->prev_state));
if (strlen(scst_alua_state_name(osi[j].new_state)) >= sizeof(descr->new_state)) {
PRINT_ERROR("new state too long (%d)", osi[j].new_state);
goto out_too_long;
}
strlcpy(descr->new_state, scst_alua_state_name(osi[j].new_state),
sizeof(descr->new_state));
if (strlen(dg->name) >= sizeof(descr->dg_name)) {
PRINT_ERROR("dg_name too long (%s)", dg->name);
goto out_too_long;
}
strlcpy(descr->dg_name, dg->name, sizeof(descr->dg_name));
if (strlen(osi[j].tg->name) >= sizeof(descr->tg_name)) {
PRINT_ERROR("tg_name too long (%s)", osi[j].tg->name);
goto out_too_long;
}
strlcpy(descr->tg_name, osi[j].tg->name,
sizeof(descr->tg_name));
descr->group_id = osi[j].group_id;
TRACE_DBG("group_id %u, prev_state %s, new_state %s, dg_name %s, "
"tg_name %s", descr->group_id, descr->prev_state,
descr->new_state, descr->dg_name, descr->tg_name);
valid_desc_count++;
descr++;
}
payload->stpg_descriptors_cnt = valid_desc_count;
if (valid_desc_count > 0) {
struct scst_dg_dev *dgd;
struct scst_stpg_wait *wait;
int rc;
dg->stpg_rel_tgt_id = cmd->tgt->rel_tgt_id;
dg->stpg_transport_id = kmemdup(cmd->sess->transport_id,
scst_tid_size(cmd->sess->transport_id), GFP_KERNEL);
if (dg->stpg_transport_id == NULL) {
PRINT_ERROR("Unable to duplicate stpg_transport_id");
goto out_free_event;
}
wait = kzalloc(sizeof(*wait), GFP_KERNEL);
if (wait == NULL) {
PRINT_ERROR("Unable to allocate STPG wait struct "
"(size %zd)", sizeof(*wait));
scst_set_busy(cmd);
res = -ENOMEM;
goto out_free_tr_id;
}
atomic_set(&wait->stpg_wait_left, 1);
wait->event_entry = event_entry;
event_entry->event_notify_fn = scst_event_stpg_notify_fn;
event_entry->notify_fn_priv = cmd;
mutex_lock(&scst_dg_mutex);
list_for_each_entry(dgd, &dg->dev_list, entry) {
if (dgd->dev == dev)
continue;
TRACE_DBG("STPG: ext blocking dev %s", dgd->dev->virt_name);
atomic_inc(&wait->stpg_wait_left);
spin_lock_bh(&dev->dev_lock);
WARN_ON(dgd->dev->stpg_ext_blocked);
dgd->dev->stpg_ext_blocked = 1;
spin_unlock_bh(&dev->dev_lock);
rc = scst_ext_block_dev(dgd->dev, false,
scst_stpg_ext_blocking_done, (uint8_t *)&wait,
sizeof(wait));
if (rc != 0) {
TRACE_DBG("scst_ext_block_dev() returned %d, "
"stepping back (cmd %p)", rc, cmd);
wait->status = rc;
wait->dg = dg;
atomic_dec(&wait->stpg_wait_left);
spin_lock_bh(&dev->dev_lock);
WARN_ON(dgd->dev->stpg_ext_blocked);
dgd->dev->stpg_ext_blocked = 0;
spin_unlock_bh(&dev->dev_lock);
break;
}
}
mutex_unlock(&scst_dg_mutex);
scst_stpg_check_blocking_done(wait);
/* !! cmd can be already dead here !! */
} else {
TRACE_DBG("Nothing to do");
goto out_free_event;
}
res = 0;
out_free:
kfree(osi);
out:
TRACE_EXIT_RES(res);
return res;
out_unlock_fail:
mutex_unlock(&scst_dg_mutex);
out_unlock_sm_fail:
mutex_unlock(&scst_mutex);
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_set_target_pgs_failed));
out_put:
scst_put_buf_full(cmd, buf);
goto out_free;
out_too_long:
scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_set_target_pgs_failed));
res = -EOVERFLOW;
out_free_tr_id:
kfree(dg->stpg_transport_id);
out_free_event:
kfree(event_entry);
goto out_free;
}
EXPORT_SYMBOL_GPL(scst_tg_set_group_info);
+2
View File
@@ -0,0 +1,2 @@
obj-$(CONFIG_SCST_LOCAL) += scst_local.o
+6 -14
View File
@@ -88,7 +88,7 @@ static unsigned long scst_local_trace_flag = SCST_LOCAL_DEFAULT_LOG_FLAGS;
#define scsi_bufflen(cmd) ((cmd)->request_bufflen)
#endif
#define SCST_LOCAL_VERSION "3.1"
#define SCST_LOCAL_VERSION "3.2"
static const char *scst_local_version_date = "20110901";
/* Some statistics */
@@ -446,11 +446,7 @@ static ssize_t scst_local_scsi_transport_version_store(struct kobject *kobj,
if (!tgt)
goto out_up;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
res = kstrtoul(buffer, 0, &val);
#else
res = strict_strtoul(buffer, 0, &val);
#endif
if (res != 0) {
PRINT_ERROR("strtoul() for %s failed: %zd", buffer, res);
goto out_up;
@@ -516,11 +512,7 @@ static ssize_t scst_local_phys_transport_version_store(struct kobject *kobj,
if (!tgt)
goto out_up;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
res = kstrtoul(buffer, 0, &val);
#else
res = strict_strtoul(buffer, 0, &val);
#endif
if (res != 0) {
PRINT_ERROR("strtoul() for %s failed: %zd", buffer, res);
goto out_up;
@@ -778,6 +770,7 @@ static ssize_t scst_local_sysfs_mgmt_cmd(char *buf)
res = __scst_local_add_adapter(tgt, session_name, true);
} else if (strcasecmp("del_session", command) == 0) {
struct scst_local_sess *s, *sess = NULL;
list_for_each_entry(s, &tgt->sessions_list,
sessions_list_entry) {
if (strcmp(s->scst_sess->initiator_name, session_name) == 0) {
@@ -897,8 +890,7 @@ static void scst_local_copy_sense(struct scsi_cmnd *cmnd, struct scst_cmd *scst_
TRACE_ENTRY();
scst_cmnd_sense_len = (SCSI_SENSE_BUFFERSIZE < scst_cmnd_sense_len ?
SCSI_SENSE_BUFFERSIZE : scst_cmnd_sense_len);
scst_cmnd_sense_len = min(scst_cmnd_sense_len, SCSI_SENSE_BUFFERSIZE);
memcpy(cmnd->sense_buffer, scst_cmd_get_sense_buffer(scst_cmnd),
scst_cmnd_sense_len);
@@ -1671,7 +1663,7 @@ static int scst_local_driver_probe(struct device *dev)
TRACE_DBG("sess %p", sess);
hpnt = scsi_host_alloc(&scst_lcl_ini_driver_template, sizeof(*sess));
if (NULL == hpnt) {
if (hpnt == NULL) {
PRINT_ERROR("%s", "scsi_register() failed");
ret = -ENODEV;
goto out;
@@ -1828,7 +1820,7 @@ static int __scst_local_add_adapter(struct scst_local_tgt *tgt,
/* It's read-mostly, so cache alignment isn't needed */
sess = kzalloc(sizeof(*sess), GFP_KERNEL);
if (NULL == sess) {
if (sess == NULL) {
PRINT_ERROR("Unable to alloc scst_lcl_host (size %zu)",
sizeof(*sess));
res = -ENOMEM;
@@ -1947,7 +1939,7 @@ static int scst_local_add_target(const char *target_name,
TRACE_ENTRY();
tgt = kzalloc(sizeof(*tgt), GFP_KERNEL);
if (NULL == tgt) {
if (tgt == NULL) {
PRINT_ERROR("Unable to alloc tgt (size %zu)", sizeof(*tgt));
res = -ENOMEM;
goto out;
+3 -11
View File
@@ -211,17 +211,7 @@ fe80:0000:0000:0000:0002:c903:0005:f350
Session names
-------------
The name assigned by the ib_srpt target driver to a session depends on the
mode in which it is operating. If one_target_per_port=y then the source port
GID is used as the session name. If one_target_per_port=n then the 128-bit SRP
initiator port identifier is used as the session name. This identifier is sent
by the SRP initiator to the SRP target via the SRP_LOGIN_REQ information unit.
The Linux SRP initiator (ib_srp) generates the initiator port identifier as
follows:
- The first eight bytes are the identifier extension ('initiator_ext' parameter
specified in the login string echoed into the sysfs file 'add_target').
- The last eight bytes are the GUID of the initiator HCA port used to
communicate with the target.
The ib_srpt target driver uses the source port GID as session name.
An example:
@@ -242,7 +232,9 @@ fe80:0000:0000:0000:0002:c903:0003:cca8
$ (cd /sys/kernel/scst_tgt/targets/ib_srpt/[0-9a-f]* && ls -d sessions/*)
sessions/fe80:0000:0000:0000:0002:c903:0003:cca7
sessions/fe80:0000:0000:0000:0002:c903:0003:cca8
sessions/fe80:0000:0000:0000:0002:c903:0005:f34b
sessions/fe80:0000:0000:0000:0002:c903:0005:f34c
LUN masking
+5 -15
View File
@@ -80,7 +80,7 @@
/* Name of this kernel module. */
#define DRV_NAME "ib_srpt"
#define DRV_VERSION "3.1.0-pre#" __stringify(OFED_FLAVOR)
#define DRV_VERSION "3.2.0-pre#" __stringify(OFED_FLAVOR)
#define DRV_RELDATE "(not yet released)"
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
/* Flags to be used in SCST debug tracing statements. */
@@ -169,16 +169,6 @@ module_param(use_node_guid_in_target_name, bool, 0444);
MODULE_PARM_DESC(use_node_guid_in_target_name,
"Use HCA node GUID as SCST target name.");
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) \
|| defined(RHEL_MAJOR) && RHEL_MAJOR -0 <= 5
static int one_target_per_port = true;
#else
static bool one_target_per_port = true;
#endif
module_param(one_target_per_port, bool, 0444);
MODULE_PARM_DESC(one_target_per_port,
"One SCST target per HCA port instead of one per HCA.");
static struct rdma_cm_id *rdma_cm_id;
static int srpt_get_u64_x(char *buffer, struct kernel_param *kp)
@@ -862,7 +852,7 @@ static struct srpt_ioctx **srpt_alloc_ioctx_ring(struct srpt_device *sdev,
WARN_ON(ioctx_size != sizeof(struct srpt_recv_ioctx) &&
ioctx_size != sizeof(struct srpt_send_ioctx));
ring = kmalloc(ring_size * sizeof(ring[0]), GFP_KERNEL);
ring = kmalloc_array(ring_size, sizeof(ring[0]), GFP_KERNEL);
if (!ring)
goto out;
for (i = 0; i < ring_size; ++i) {
@@ -1164,8 +1154,8 @@ static int srpt_get_desc_tbl(struct srpt_recv_ioctx *recv_ioctx,
if (ioctx->n_rbuf == 1)
ioctx->rbufs = &ioctx->single_rbuf;
else {
ioctx->rbufs =
kmalloc(ioctx->n_rbuf * sizeof(*db), GFP_ATOMIC);
ioctx->rbufs = kmalloc_array(ioctx->n_rbuf,
sizeof(*db), GFP_ATOMIC);
if (!ioctx->rbufs) {
ioctx->n_rbuf = 0;
ret = -ENOMEM;
@@ -1650,7 +1640,7 @@ static int srpt_build_cmd_rsp(struct srpt_rdma_ch *ch,
if (!scst_sense_valid(sense_data)) {
sense_data_len = 0;
} else {
BUILD_BUG_ON(MIN_MAX_RSP_SIZE <= sizeof(*srp_rsp));
BUILD_BUG_ON(sizeof(*srp_rsp) >= MIN_MAX_RSP_SIZE);
max_sense_len = ch->max_ti_iu_len - sizeof(*srp_rsp);
if (sense_data_len > max_sense_len) {
pr_warn("truncated sense data from %d to %d bytes\n",
+89
View File
@@ -0,0 +1,89 @@
#
# Common makefile for SCSI target mid-level and its drivers
#
# Copyright (C) 2004 - 2015 Vladislav Bolkhovitin <vst@vlnb.net>
# Copyright (C) 2004 - 2005 Leonid Stoljar
# Copyright (C) 2007 - 2015 SanDisk Corporation
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation, version 2
# of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
SHELL=/bin/bash
# Decide to use which kernel src. If not specified, is current running kernel.
#export KDIR=/usr/src/linux-2.6
FILEIO_DIR=fileio
STPGD_DIR=stpgd
EVENTS_DIR=events
all:
cd $(FILEIO_DIR) && $(MAKE) $@
cd $(STPGD_DIR) && $(MAKE) $@
# cd $(EVENTS_DIR) && $(MAKE) $@
install:
cd $(FILEIO_DIR) && $(MAKE) $@
cd $(STPGD_DIR) && $(MAKE) $@
# cd $(EVENTS_DIR) && $(MAKE) $@
uninstall:
cd $(FILEIO_DIR) && $(MAKE) $@
cd $(STPGD_DIR) && $(MAKE) $@
cd $(EVENTS_DIR) && $(MAKE) $@
clean:
cd $(FILEIO_DIR) && $(MAKE) $@
cd $(STPGD_DIR) && $(MAKE) $@
cd $(EVENTS_DIR) && $(MAKE) $@
extraclean:
cd $(FILEIO_DIR) && $(MAKE) $@
cd $(STPGD_DIR) && $(MAKE) $@
cd $(EVENTS_DIR) && $(MAKE) $@
2release:
cd $(FILEIO_DIR) && $(MAKE) $@
cd $(STPGD_DIR) && $(MAKE) $@
cd $(EVENTS_DIR) && $(MAKE) $@
2debug:
cd $(FILEIO_DIR) && $(MAKE) $@
cd $(STPGD_DIR) && $(MAKE) $@
cd $(EVENTS_DIR) && $(MAKE) $@
2perf:
cd $(FILEIO_DIR) && $(MAKE) $@
cd $(STPGD_DIR) && $(MAKE) $@
cd $(EVENTS_DIR) && $(MAKE) $@
disable_proc:
cd $(FILEIO_DIR) && $(MAKE) $@
cd $(STPGD_DIR) && $(MAKE) $@
cd $(EVENTS_DIR) && $(MAKE) $@
enable_proc:
cd $(FILEIO_DIR) && $(MAKE) $@
cd $(STPGD_DIR) && $(MAKE) $@
cd $(EVENTS_DIR) && $(MAKE) $@
help:
@echo " all (the default) : make all"
@echo " clean : clean files"
@echo " extraclean : clean + clean dependencies"
@echo " install : install"
@echo " uninstall : uninstall"
@echo " Notes :"
@echo " - install and uninstall must be made as root."
@echo " - be sure to compile qla against the correct initiator"
@echo " driver. Read its README for details."
.PHONY: all install uninstall clean extraclean help 2release 2debug 2perf disable_proc enable_proc
+102
View File
@@ -0,0 +1,102 @@
ifndef PREFIX
PREFIX=/usr/local
endif
SHELL=/bin/bash
SRCS_F = events.c debug.c
OBJS_F = $(SRCS_F:.c=.o)
#SRCS_C =
#OBJS_C = $(SRCS_C:.c=.o)
SCST_INC_DIR := $(shell if [ -e "$$PWD/../../scst" ]; \
then echo "$$PWD/../../scst/include"; \
else echo "$(DESTDIR)$(PREFIX)/include/scst"; fi)
DEBUG_INC_DIR := ../include
INSTALL_DIR := $(DESTDIR)$(PREFIX)/bin/scst
CFLAGS += -O2 -Wall -Wextra -Wno-unused-parameter -Wstrict-prototypes \
-I$(SCST_INC_DIR) -I$(DEBUG_INC_DIR) -D_GNU_SOURCE -D__USE_FILE_OFFSET64 \
-D__USE_LARGEFILE64
PROGS = events
LIBS = -lpthread
CFLAGS += -DEXTRACHECKS
#CFLAGS += -DTRACING
CFLAGS += -DDEBUG -g -fno-inline -fno-inline-functions
CFLAGS += -W -Wno-unused-parameter
CFLAGS += $(LOCAL_CFLAGS)
#CFLAGS += -DDEBUG_NOMEM
all: $(PROGS)
events: .depend_f $(OBJS_F)
$(CC) $(OBJS_F) $(LIBS) $(LOCAL_LD_FLAGS) -o $@
#cdrom_tgt: .depend_c $(OBJS_C)
# $(CC) $(OBJS_C) $(LIBS) $(LOCAL_LD_FLAGS) -o $@
ifeq (.depend_f,$(wildcard .depend_f))
-include .depend_f
endif
#ifeq (.depend_c,$(wildcard .depend_c))
#-include .depend_c
#endif
%.o: %.c Makefile
$(CC) -c -o $(@) $(CFLAGS) $(<)
.depend_f:
$(CC) -M $(CFLAGS) $(SRCS_F) >$(@)
#.depend_c:
# $(CC) -M $(CFLAGS) $(SRCS_C) >$(@)
install: all
install -d $(INSTALL_DIR)
install -m 755 $(PROGS) $(INSTALL_DIR)
uninstall:
rm -f $(INSTALL_DIR)/$(PROGS)
rm -rf $(INSTALL_DIR)
clean:
rm -f *.o $(PROGS) .depend*
extraclean: clean
rm -f *.orig *.rej
2release:
sed -i.aa s/"^C\?FLAGS += \-DEXTRACHECKS"/"#CFLAGS += \-DEXTRACHECKS"/ Makefile
grep "^#CFLAGS += \-DEXTRACHECKS" Makefile >/dev/null
sed -i.aa s/"^#\?CFLAGS += \-DTRACING"/"CFLAGS += \-DTRACING"/ Makefile
grep "^CFLAGS += \-DTRACING" Makefile >/dev/null
sed -i.aa s/"^C\?FLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/"#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/ Makefile
grep "^#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions" Makefile >/dev/null
rm Makefile.aa
2debug:
sed -i.aa s/"^#\?CFLAGS += \-DEXTRACHECKS"/"CFLAGS += \-DEXTRACHECKS"/ Makefile
grep "^CFLAGS += \-DEXTRACHECKS" Makefile >/dev/null
sed -i.aa s/"^C\?FLAGS += \-DTRACING"/"#CFLAGS += \-DTRACING"/ Makefile
grep "^#CFLAGS += \-DTRACING" Makefile >/dev/null
sed -i.aa s/"^#\?CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/"CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/ Makefile
grep "^CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions" Makefile >/dev/null
rm Makefile.aa
2perf:
sed -i.aa s/"^C\?FLAGS += \-DEXTRACHECKS"/"#CFLAGS += \-DEXTRACHECKS"/ Makefile
grep "^#CFLAGS += \-DEXTRACHECKS" Makefile >/dev/null
sed -i.aa s/"^C\?FLAGS += \-DTRACING"/"#CFLAGS += \-DTRACING"/ Makefile
grep "^#CFLAGS += \-DTRACING" Makefile >/dev/null
sed -i.aa s/"^C\?FLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/"#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/ Makefile
grep "^#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions" Makefile >/dev/null
rm Makefile.aa
release-archive:
../../scripts/generate-release-archive events "$$(sed -n 's/^#define[[:blank:]]VERSION_STR[[:blank:]]*\"\([^\"]*\)\".*/\1/p' events.c)"
.PHONY: all install uninstall clean extraclean 2release 2debug 2perf
+2
View File
@@ -0,0 +1,2 @@
This is test/demo application for SCST events subsystem. For more
information about this subsystem, see scst_event.h file.
+1
View File
@@ -0,0 +1 @@
#include "../include/debug.c"
+326
View File
@@ -0,0 +1,326 @@
/*
* events.c
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <getopt.h>
#include <malloc.h>
#include <inttypes.h>
#include <stdbool.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/user.h>
#include <sys/poll.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include "version.h"
#include "debug.h"
#include "scst_event.h"
char *app_name;
#if defined(DEBUG) || defined(TRACING)
#ifdef DEBUG
/*#define DEFAULT_LOG_FLAGS (TRACE_ALL & ~TRACE_MEMORY & ~TRACE_BUFF \
& ~TRACE_FUNCTION)
#define DEFAULT_LOG_FLAGS (TRACE_ALL & ~TRACE_MEMORY & ~TRACE_BUFF & \
~TRACE_SCSI & ~TRACE_SCSI_SERIALIZING & ~TRACE_DEBUG)
*/
#define DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MINOR | TRACE_PID | \
TRACE_FUNCTION | TRACE_SPECIAL | TRACE_MGMT | TRACE_MGMT_DEBUG | \
TRACE_TIME)
#define TRACE_SN(args...) TRACE(TRACE_SCSI_SERIALIZING, args)
#else /* DEBUG */
# ifdef TRACING
#define DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MGMT | \
TRACE_TIME | TRACE_SPECIAL)
# else
#define DEFAULT_LOG_FLAGS 0
# endif
#endif /* DEBUG */
bool log_daemon = false; /* needed for the tracing infrastructure */
unsigned long trace_flag = DEFAULT_LOG_FLAGS;
#endif /* defined(DEBUG) || defined(TRACING) */
static struct option const long_options[] = {
{"allowed_event", required_argument, 0, 'e'},
{"allowed_issuer", required_argument, 0, 'i'},
{"non_blocking", no_argument, 0, 'n'},
#if defined(DEBUG) || defined(TRACING)
{"debug", required_argument, 0, 'd'},
#endif
{"version", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0},
};
#define MAX_ALLOWED_EVENTS 16
static struct scst_event allowed_events[MAX_ALLOWED_EVENTS];
static int allowed_events_num;
static int non_blocking;
static void usage(void)
{
printf("Usage: %s [OPTIONS]\n", app_name);
printf("\nSCST events testing program\n");
printf(" -e, --allowed_event=code Allowed event code, 0 - any event\n");
printf(" -i, --allowed_issuer=issuer Allowed issuer, * - any issuer\n");
printf(" -n, --non_blocking Use non-blocking operations\n");
#if defined(DEBUG) || defined(TRACING)
printf(" -d, --debug=level Debug tracing level\n");
#endif
}
static void handle_tm_received(struct scst_event_user *event_user)
{
struct scst_event_tm_fn_received_payload *p = (struct scst_event_tm_fn_received_payload *)event_user->out_event.payload;
printf("fn %d, device %s\n", p->fn, p->device_name);
return;
}
int main(int argc, char **argv)
{
int res = 0, i;
int ch, longindex;
int event_fd;
uint8_t event_user_buf[10240];
struct scst_event_user *event_user = (struct scst_event_user *)event_user_buf;
struct pollfd pl;
setlinebuf(stdout);
res = debug_init();
if (res != 0)
goto out;
app_name = argv[0];
while ((ch = getopt_long(argc, argv, "+e:i:d:nhv",
long_options, &longindex)) >= 0) {
switch (ch) {
case 'e':
if (allowed_events[allowed_events_num].event_code != 0) {
allowed_events_num++;
if (allowed_events_num >= MAX_ALLOWED_EVENTS) {
PRINT_ERROR("Too many allowed events %d",
allowed_events_num);
exit(1);
}
}
allowed_events[allowed_events_num].event_code = atoi(optarg);
break;
case 'i':
if (allowed_events[allowed_events_num].issuer_name[0] != '\0') {
allowed_events_num++;
if (allowed_events_num >= MAX_ALLOWED_EVENTS) {
PRINT_ERROR("Too many allowed events %d",
allowed_events_num);
exit(1);
}
}
strncpy(allowed_events[allowed_events_num].issuer_name,
optarg, sizeof(allowed_events[allowed_events_num].issuer_name));
allowed_events[allowed_events_num].issuer_name[
sizeof(allowed_events[allowed_events_num].issuer_name)-1] = '\0';
break;
case 'n':
non_blocking = 1;
break;
#if defined(DEBUG) || defined(TRACING)
case 'd':
trace_flag = strtol(optarg, (char **)NULL, 0);
break;
#endif
case 'v':
printf("%s version %s\n", app_name, VERSION_STR);
goto out_done;
default:
goto out_usage;
}
}
if (allowed_events_num == 0) {
if ((allowed_events[0].event_code == 0) &&
(allowed_events[0].issuer_name[0] == '\0'))
allowed_events[0].issuer_name[0] = '*';
}
allowed_events_num++;
#ifdef DEBUG
PRINT_INFO("trace_flag %lx", trace_flag);
#endif
if (non_blocking)
PRINT_INFO("%s", "NON-BLOCKING");
event_fd = open(SCST_EVENT_DEV, O_RDWR | (non_blocking ? O_NONBLOCK : 0));
if (event_fd < 0) {
res = -errno;
PRINT_ERROR("Unable to open SCST event device %s (%s)",
SCST_EVENT_DEV, strerror(-res));
goto out_done;
}
memset(&pl, 0, sizeof(pl));
pl.fd = event_fd;
pl.events = POLLIN;
for (i = 0; i < allowed_events_num; i++) {
struct scst_event e;
PRINT_INFO("Setting allowed event code %d, issuer_name %s",
allowed_events[i].event_code,
allowed_events[i].issuer_name);
memset(&e, 0, sizeof(e));
e.event_code = allowed_events[i].event_code;
strncpy(e.issuer_name, allowed_events[i].issuer_name,
sizeof(e.issuer_name));
e.issuer_name[sizeof(e.issuer_name)-1] = '\0';
res = ioctl(event_fd, SCST_EVENT_ALLOW_EVENT, &e);
if (res != 0) {
PRINT_ERROR("SCST_EVENT_ALLOW_EVENT failed: %s (res %d)",
strerror(errno), res);
goto out_done;
}
}
while (1) {
memset(event_user_buf, 0, sizeof(event_user_buf));
event_user->max_event_size = sizeof(event_user_buf);
res = ioctl(event_fd, SCST_EVENT_GET_NEXT_EVENT, event_user);
if (res != 0) {
res = errno;
switch (res) {
case ESRCH:
case EBUSY:
TRACE_MGMT_DBG("SCST_EVENT_GET_NEXT_EVENT returned "
"%d (%s)", res, strerror(res));
/* go through */
case EINTR:
continue;
case EAGAIN:
TRACE_DBG("SCST_EVENT_GET_NEXT_EVENT, returned "
"EAGAIN (%d)", res);
if (non_blocking)
break;
else
continue;
default:
PRINT_ERROR("SCST_EVENT_GET_NEXT_EVENT failed: %s (res %d)",
strerror(errno), res);
goto out_done;
}
again_poll:
res = poll(&pl, 1, 2000);
if (res > 0)
continue;
else if (res == 0)
goto again_poll;
else {
res = errno;
switch (res) {
case ESRCH:
case EBUSY:
case EAGAIN:
TRACE_MGMT_DBG("poll() returned %d "
"(%s)", res, strerror(res));
case EINTR:
goto again_poll;
default:
PRINT_ERROR("poll() failed: %s", strerror(res));
#if 1
goto again_poll;
#else
goto out_close;
#endif
}
}
}
PRINT_INFO("\nevent_code %d, issuer_name %s",
event_user->out_event.event_code,
event_user->out_event.issuer_name);
if (event_user->out_event.payload_len != 0)
PRINT_BUFFER("payoad", event_user->out_event.payload,
event_user->out_event.payload_len);
PRINT_INFO("%s", "");
if (event_user->out_event.event_code == 0x12345) {
struct scst_event_notify_done d;
PRINT_INFO("%s", "Press any key to send reply to event "
"0x12345");
getchar();
memset(&d, 0, sizeof(d));
d.event_id = event_user->out_event.event_id;
d.status = -19;
res = ioctl(event_fd, SCST_EVENT_NOTIFY_DONE, &d);
if (res != 0)
PRINT_ERROR("SCST_EVENT_NOTIFY_DONE failed: %s "
"(res %d)", strerror(errno), res);
} else if (event_user->out_event.event_code == SCST_EVENT_TM_FN_RECEIVED)
handle_tm_received(event_user);
else if (event_user->out_event.event_code == SCST_EVENT_TM_FN_RECEIVED) {
struct scst_event_notify_done d;
memset(&d, 0, sizeof(d));
d.event_id = event_user->out_event.event_id;
d.status = -20;
res = ioctl(event_fd, SCST_EVENT_NOTIFY_DONE, &d);
if (res != 0)
PRINT_ERROR("SCST_EVENT_NOTIFY_DONE failed: %s "
"(res %d)", strerror(errno), res);
}
#if 0
{
struct scst_event e;
PRINT_INFO("Deleting allowed event code %d, issuer_name %s",
allowed_events[0].event_code,
allowed_events[0].issuer_name);
memset(&e, 0, sizeof(e));
e.event_code = allowed_events[0].event_code;
strncpy(e.issuer_name, allowed_events[0].issuer_name,
sizeof(e.issuer_name));
e.issuer_name[sizeof(e.issuer_name)-1] = '\0';
res = ioctl(event_fd, SCST_EVENT_DISALLOW_EVENT, &e);
if (res != 0) {
PRINT_ERROR("SCST_EVENT_DISALLOW_EVENT failed: "
"%s (res %d)", strerror(errno), res);
}
}
#endif
}
out_done:
debug_done();
out:
return res;
out_usage:
usage();
goto out_done;
}
+3 -1
View File
@@ -29,10 +29,11 @@ OBJS_F = $(SRCS_F:.c=.o)
SCST_INC_DIR := $(shell if [ -e "$$PWD/../../scst" ]; \
then echo "$$PWD/../../scst/include"; \
else echo "$(DESTDIR)$(PREFIX)/include/scst"; fi)
DEBUG_INC_DIR := ../include
INSTALL_DIR := $(DESTDIR)$(PREFIX)/bin/scst
CFLAGS += -O2 -Wall -Wextra -Wno-unused-parameter -Wstrict-prototypes \
-I$(SCST_INC_DIR) -D_GNU_SOURCE -D__USE_FILE_OFFSET64 \
-I$(SCST_INC_DIR) -I$(DEBUG_INC_DIR) -D_GNU_SOURCE -D__USE_FILE_OFFSET64 \
-D__USE_LARGEFILE64
PROGS = fileio_tgt
LIBS = -lpthread
@@ -48,6 +49,7 @@ CFLAGS += $(LOCAL_CFLAGS)
#CFLAGS += -DDEBUG_TM_IGNORE
#CFLAGS += -DDEBUG_TM_IGNORE -DDEBUG_TM_FN_IGNORE
#CFLAGS += -DDEBUG_TM_IGNORE_ALL
CFLAGS += -DDEBUG_EXT_COPY_REMAP
all: $(PROGS)
+1 -1
View File
@@ -1,7 +1,7 @@
User space FILEIO handler
=========================
Version 3.1.0, XX XXXXX 2014
Version 3.2.0, XX XXXXX 2014
----------------------------
User space program fileio_tgt uses interface of SCST's scst_user dev
+187 -22
View File
@@ -47,6 +47,7 @@ static int exec_fsync(struct vdisk_cmd *vcmd);
static void exec_read(struct vdisk_cmd *vcmd, loff_t loff);
static void exec_write(struct vdisk_cmd *vcmd, loff_t loff);
static void exec_verify(struct vdisk_cmd *vcmd, loff_t loff);
static void exec_write_same(struct vdisk_cmd *vcmd);
static int open_dev_fd(struct vdisk_dev *dev)
{
@@ -68,11 +69,31 @@ static int open_dev_fd(struct vdisk_dev *dev)
return res;
}
static inline void set_cmd_error_status(struct scst_user_scsi_cmd_reply_exec *reply,
static void set_resp_data_len(struct vdisk_cmd *vcmd, int32_t resp_data_len)
{
struct scst_user_scsi_cmd_reply_exec *reply = &vcmd->reply->exec_reply;
TRACE_ENTRY();
if (vcmd->may_need_to_free_pbuf && (resp_data_len == 0)) {
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
free((void *)(unsigned long)cmd->pbuf);
cmd->pbuf = 0;
reply->pbuf = 0;
}
reply->resp_data_len = resp_data_len;
TRACE_EXIT();
return;
}
static inline void set_cmd_error_status(struct vdisk_cmd *vcmd,
int status)
{
struct scst_user_scsi_cmd_reply_exec *reply = &vcmd->reply->exec_reply;
reply->status = status;
reply->resp_data_len = 0;
set_resp_data_len(vcmd, 0);
return;
}
@@ -107,7 +128,7 @@ void set_cmd_error(struct vdisk_cmd *vcmd, int key, int asc, int ascq)
EXTRACHECKS_BUG_ON(vcmd->cmd->subcode != SCST_USER_EXEC);
set_cmd_error_status(reply, SAM_STAT_CHECK_CONDITION);
set_cmd_error_status(vcmd, SAM_STAT_CHECK_CONDITION);
reply->sense_len = set_sense(vcmd->sense, sizeof(vcmd->sense), key,
asc, ascq);
reply->psense_buffer = (unsigned long)vcmd->sense;
@@ -116,11 +137,11 @@ void set_cmd_error(struct vdisk_cmd *vcmd, int key, int asc, int ascq)
return;
}
void set_busy(struct scst_user_scsi_cmd_reply_exec *reply)
void set_busy(struct vdisk_cmd *vcmd)
{
TRACE_ENTRY();
set_cmd_error_status(reply, SAM_STAT_TASK_SET_FULL);
set_cmd_error_status(vcmd, SAM_STAT_TASK_SET_FULL);
TRACE_MGMT_DBG("%s", "Sending QUEUE_FULL status");
TRACE_EXIT();
@@ -211,6 +232,9 @@ static int do_exec(struct vdisk_cmd *vcmd)
TRACE_ENTRY();
/* Must be reinitialized each time to avoid crash on stale value */
vcmd->may_need_to_free_pbuf = 0;
switch(cmd->queue_type) {
case SCST_CMD_QUEUE_ORDERED:
TRACE(TRACE_ORDER, "ORDERED cmd_h %d", vcmd->cmd->cmd_h);
@@ -250,6 +274,7 @@ static int do_exec(struct vdisk_cmd *vcmd)
else
#endif
cmd->pbuf = (unsigned long)dev->alloc_fn(cmd->alloc_len);
vcmd->may_need_to_free_pbuf = 1;
TRACE_MEM("Buf %"PRIx64" alloced, len %d", cmd->pbuf,
cmd->alloc_len);
reply->pbuf = cmd->pbuf;
@@ -257,7 +282,7 @@ static int do_exec(struct vdisk_cmd *vcmd)
TRACE(TRACE_OUT_OF_MEM, "Unable to allocate buffer "
"(len %d)", cmd->alloc_len);
#ifndef DEBUG_NOMEM
set_busy(reply);
set_busy(vcmd);
#endif
goto out;
}
@@ -408,6 +433,10 @@ static int do_exec(struct vdisk_cmd *vcmd)
case READ_CAPACITY:
exec_read_capacity(vcmd);
break;
case WRITE_SAME_10:
case WRITE_SAME_16:
exec_write_same(vcmd);
break;
case SERVICE_ACTION_IN_16:
if ((cmd->cdb[1] & 0x1f) == SAI_READ_CAPACITY_16)
exec_read_capacity16(vcmd);
@@ -519,6 +548,49 @@ static int do_on_free_cmd(struct vdisk_cmd *vcmd)
return res;
}
#ifdef DEBUG_EXT_COPY_REMAP
static int do_ext_copy_remap(struct vdisk_cmd *vcmd)
{
struct scst_user_get_cmd *cmd = vcmd->cmd;
struct scst_user_ext_copy_remap *rcmd = &cmd->remap_cmd;
struct scst_user_reply_cmd *reply = vcmd->reply;
struct scst_user_ext_copy_reply_remap *rreply = &reply->remap_reply;
static struct scst_user_ext_copy_data_descr d[1];
int res = 0;
TRACE_ENTRY();
TRACE_DBG("Ext copy remap cmd %d", cmd->cmd_h);
memset(reply, 0, sizeof(*reply));
reply->cmd_h = cmd->cmd_h;
reply->subcode = cmd->subcode;
memset(rreply, 0, sizeof(*rreply));
/* It's only a debug code, so it's OK to use static descr */
memset(d, 0, sizeof(d));
d[0].data_len = rcmd->data_descr.data_len;
d[0].src_lba = rcmd->data_descr.src_lba;
d[0].dst_lba = rcmd->data_descr.dst_lba;
#if 1
rreply->remap_descriptors = (unsigned long)d;
rreply->remap_descriptors_len = sizeof(d);
#else
rreply->status = SAM_STAT_CHECK_CONDITION;
rreply->sense_len = set_sense(vcmd->sense, sizeof(vcmd->sense),
SCST_LOAD_SENSE(scst_sense_data_protect));
rreply->psense_buffer = (unsigned long)vcmd->sense;
#endif
TRACE_EXIT_RES(res);
return res;
}
#endif
static int do_tm(struct vdisk_cmd *vcmd, int done)
{
struct scst_user_get_cmd *cmd = vcmd->cmd;
@@ -663,6 +735,12 @@ static int process_cmd(struct vdisk_cmd *vcmd)
res = do_on_free_cmd(vcmd);
break;
#ifdef DEBUG_EXT_COPY_REMAP
case SCST_USER_EXT_COPY_REMAP:
res = do_ext_copy_remap(vcmd);
break;
#endif
case SCST_USER_TASK_MGMT_RECEIVED:
res = do_tm(vcmd, 0);
break;
@@ -699,7 +777,14 @@ void *main_loop(void *arg)
struct vdisk_dev *dev = (struct vdisk_dev *)arg;
struct scst_user_get_cmd cmd;
struct scst_user_reply_cmd reply;
struct vdisk_cmd vcmd = { -1, &cmd, dev, &reply, {0}};
struct vdisk_cmd vcmd = {
.fd = -1,
.cmd = &cmd,
.dev = dev,
.may_need_to_free_pbuf = 0,
.reply = &reply,
.sense = {0}
};
int scst_usr_fd = dev->scst_usr_fd;
struct pollfd pl;
#define MULTI_CMDS_CNT 2
@@ -741,6 +826,7 @@ void *main_loop(void *arg)
}
}
#endif
if (use_multi) {
TRACE_DBG("preplies %p (first: %p), replies_cnt %d, "
"replies_done %d, cmds_cnt %d", (void *)multi.multi_cmd.preplies,
@@ -901,11 +987,13 @@ static void exec_inquiry(struct vdisk_cmd *vcmd)
uint64_t dev_id_num = gen_dev_id_num(dev);
if (0 == cmd->cdb[2]) { /* supported vital product data pages */
buf[3] = 3;
buf[3] = 5;
buf[4] = 0x0; /* this page */
buf[5] = 0x80; /* unit serial number */
buf[6] = 0x83; /* device identification */
resp_len = buf[3] + 4;
buf[7] = 0xB0; /* block limits */
buf[8] = 0xB1; /* block device characteristics */
resp_len = buf[3] + 6;
} else if (0x80 == cmd->cdb[2]) { /* unit serial number */
int usn_len = strlen(dev->usn);
buf[1] = 0x80;
@@ -952,6 +1040,55 @@ static void exec_inquiry(struct vdisk_cmd *vcmd)
buf[2] = (resp_len >> 8) & 0xFF;
buf[3] = resp_len & 0xFF;
resp_len += 4;
} else if (0xB0 == cmd->cdb[2]) {
/* Block Limits */
int max_transfer;
buf[1] = 0xB0;
buf[3] = 0x3C;
buf[5] = 0xFF; /* No MAXIMUM COMPARE AND WRITE LENGTH limit */
/* Optimal transfer granuality is PAGE_SIZE */
max_transfer = max((int)(4096/dev->block_size), (int)1);
buf[6] = (max_transfer >> 8) & 0xff;
buf[7] = max_transfer & 0xff;
/*
* Max transfer len is min of sg limit and 8M, but we
* don't have access to them here, so let's use 1M.
*/
max_transfer = 1*1024*1024;
buf[8] = (max_transfer >> 24) & 0xff;
buf[9] = (max_transfer >> 16) & 0xff;
buf[10] = (max_transfer >> 8) & 0xff;
buf[11] = max_transfer & 0xff;
/*
* Let's have optimal transfer len 512KB. Better to not
* set it at all, because we don't have such limit,
* but some initiators may not understand that (?).
* From other side, too big transfers are not optimal,
* because SGV cache supports only <4M buffers.
*/
max_transfer = min((int)max_transfer, (int)(512*1024 / dev->block_size));
buf[12] = (max_transfer >> 24) & 0xff;
buf[13] = (max_transfer >> 16) & 0xff;
buf[14] = (max_transfer >> 8) & 0xff;
buf[15] = max_transfer & 0xff;
resp_len = buf[3] + 4;
} else if (0xB1 == cmd->cdb[2]) {
int r;
/* Block Device Characteristics */
buf[1] = 0xB1;
buf[3] = 0x3C;
#if 0
if (virt_dev->rotational) {
#endif
/* 15K RPM */
r = 0x3A98;
#if 0
} else
r = 1;
#endif
buf[4] = (r >> 8) & 0xff;
buf[5] = r & 0xff;
resp_len = buf[3] + 4;
} else {
TRACE_DBG("INQUIRY: Unsupported EVPD page %x",
cmd->cdb[2]);
@@ -995,7 +1132,7 @@ static void exec_inquiry(struct vdisk_cmd *vcmd)
memcpy(address, buf, length);
if (length < reply->resp_data_len)
reply->resp_data_len = length;
set_resp_data_len(vcmd, length);
out:
TRACE_EXIT();
@@ -1019,7 +1156,7 @@ static void exec_request_sense(struct vdisk_cmd *vcmd)
memcpy(address, b, length);
if (length < reply->resp_data_len)
reply->resp_data_len = length;
set_resp_data_len(vcmd, length);
TRACE_EXIT();
return;
@@ -1260,7 +1397,7 @@ static void exec_mode_sense(struct vdisk_cmd *vcmd)
memcpy(address, buf, offset);
if (offset < reply->resp_data_len)
reply->resp_data_len = offset;
set_resp_data_len(vcmd, offset);
out:
TRACE_EXIT();
@@ -1391,7 +1528,7 @@ static void exec_read_capacity(struct vdisk_cmd *vcmd)
memcpy(address, buffer, length);
if (length < reply->resp_data_len)
reply->resp_data_len = length;
set_resp_data_len(vcmd, length);
TRACE_EXIT();
return;
@@ -1450,7 +1587,7 @@ static void exec_read_capacity16(struct vdisk_cmd *vcmd)
memcpy(address, buffer, length);
if (length < reply->resp_data_len)
reply->resp_data_len = length;
set_resp_data_len(vcmd, length);
TRACE_EXIT();
return;
@@ -1529,7 +1666,7 @@ static void exec_read_toc(struct vdisk_cmd *vcmd)
memcpy(address, buffer, off);
if (off < reply->resp_data_len)
reply->resp_data_len = off;
set_resp_data_len(vcmd, off);
out:
TRACE_EXIT();
@@ -1572,7 +1709,6 @@ static void exec_read(struct vdisk_cmd *vcmd, loff_t loff)
{
struct vdisk_dev *dev = vcmd->dev;
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
struct scst_user_scsi_cmd_reply_exec *reply = &vcmd->reply->exec_reply;
int length = cmd->bufflen;
uint8_t *address = (uint8_t*)(unsigned long)cmd->pbuf;
int fd = vcmd->fd;
@@ -1601,7 +1737,7 @@ static void exec_read(struct vdisk_cmd *vcmd, loff_t loff)
PRINT_ERROR("read() returned %"PRId64" from %d (errno %d)",
(uint64_t)err, length, errno);
if (err == -EAGAIN)
set_busy(reply);
set_busy(vcmd);
else {
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_read_error));
@@ -1609,7 +1745,7 @@ static void exec_read(struct vdisk_cmd *vcmd, loff_t loff)
goto out;
}
reply->resp_data_len = cmd->bufflen;
set_resp_data_len(vcmd, cmd->bufflen);
out:
TRACE_EXIT();
@@ -1620,7 +1756,6 @@ static void exec_write(struct vdisk_cmd *vcmd, loff_t loff)
{
struct vdisk_dev *dev = vcmd->dev;
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
struct scst_user_scsi_cmd_reply_exec *reply = &vcmd->reply->exec_reply;
loff_t err;
int length = cmd->bufflen;
uint8_t *address = (uint8_t*)(unsigned long)cmd->pbuf;
@@ -1653,7 +1788,7 @@ restart:
PRINT_ERROR("write() returned %"PRId64" from %d (errno %d, "
"cmd_h %x)", err, length, errno, vcmd->cmd->cmd_h);
if (err == -EAGAIN)
set_busy(reply);
set_busy(vcmd);
else {
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_write_error));
@@ -1682,7 +1817,6 @@ static void exec_verify(struct vdisk_cmd *vcmd, loff_t loff)
{
struct vdisk_dev *dev = vcmd->dev;
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
struct scst_user_scsi_cmd_reply_exec *reply = &vcmd->reply->exec_reply;
loff_t err;
int64_t length = cmd->bufflen;
uint8_t *address = (uint8_t *)(unsigned long)cmd->pbuf;
@@ -1735,7 +1869,7 @@ static void exec_verify(struct vdisk_cmd *vcmd, loff_t loff)
PRINT_ERROR("read() returned %"PRId64" from %"PRId64" "
"(errno %d)", (uint64_t)err, len_mem, errno);
if (err == -EAGAIN)
set_busy(reply);
set_busy(vcmd);
else {
set_cmd_error(vcmd,
SCST_LOAD_SENSE(scst_sense_read_error));
@@ -1761,3 +1895,34 @@ out:
TRACE_EXIT();
return;
}
static void exec_write_same(struct vdisk_cmd *vcmd)
{
struct vdisk_dev *dev = vcmd->dev;
struct scst_user_scsi_cmd_exec *cmd = &vcmd->cmd->exec_cmd;
struct scst_user_scsi_cmd_reply_exec *reply = &vcmd->reply->exec_reply;
uint64_t blocks = cmd->data_len >> dev->block_shift;
static struct scst_user_data_descriptor uwhere[3];
TRACE_ENTRY();
if (blocks < 2)
goto out;
/* It's only a debug code, so it's OK to use static descr */
memset(uwhere, 0, sizeof(uwhere));
uwhere[0].usdd_lba = cmd->lba;
uwhere[0].usdd_blocks = 1;
uwhere[1].usdd_lba = cmd->lba+1;
uwhere[1].usdd_blocks = blocks-1;
reply->reply_type = SCST_EXEC_REPLY_DO_WRITE_SAME;
reply->ws_descriptors_len = sizeof(uwhere);
reply->ws_descriptors = (unsigned long)uwhere;
out:
TRACE_EXIT();
return;
}
+6 -1
View File
@@ -23,10 +23,14 @@
#include "debug.h"
#ifndef WRITE_SAME_10
#define WRITE_SAME_10 0x41
#endif
/* 8 byte ASCII Vendor */
#define VENDOR "SCST_USR"
/* 4 byte ASCII Product Revision Level - left aligned */
#define FIO_REV " 310"
#define FIO_REV " 320"
#define MAX_USN_LEN (20+1) /* For '\0' */
@@ -93,6 +97,7 @@ struct vdisk_cmd
int fd;
struct scst_user_get_cmd *cmd;
struct vdisk_dev *dev;
unsigned int may_need_to_free_pbuf:1;
struct scst_user_reply_cmd *reply;
uint8_t sense[SCST_SENSE_BUFFERSIZE];
};
+1 -148
View File
@@ -1,148 +1 @@
/*
* debug.c
*
* Copyright (C) 2004 - 2015 Vladislav Bolkhovitin <vst@vlnb.net>
* Copyright (C) 2004 - 2005 Leonid Stoljar
* Copyright (C) 2007 - 2015 SanDisk Corporation
*
* Contains helper functions for execution tracing and error reporting.
* Intended to be included in main .c file.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, version 2
* of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <stdio.h>
#include <time.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
#include "debug.h"
pid_t gettid (void)
{
return syscall(__NR_gettid);
}
#if defined(DEBUG) || defined(TRACING)
#define TRACE_BUF_SIZE 512
static char trace_buf[TRACE_BUF_SIZE];
static pthread_spinlock_t trace_buf_lock;
int debug_print_prefix(unsigned long trace_flag, const char *prefix,
const char *func, int line)
{
int i = 0;
pthread_spin_lock(&trace_buf_lock);
if (trace_flag & TRACE_TIME) {
struct tm t;
time_t tt;
time(&tt);
localtime_r(&tt, &t);
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%d:%d:%d ",
t.tm_hour, t.tm_min, t.tm_sec);
}
if (trace_flag & TRACE_PID)
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "[%d]: ",
gettid());
if (prefix != NULL)
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%s:", prefix);
if (trace_flag & TRACE_FUNCTION)
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%s:", func);
if (trace_flag & TRACE_LINE)
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%i:", line);
if (i > 0)
PRINTN("%s", trace_buf);
pthread_spin_unlock(&trace_buf_lock);
return i;
}
void debug_print_buffer(const void *data, int len)
{
int z, z1, i;
const unsigned char *buf = (const unsigned char *) data;
int f = 0;
if (buf == NULL)
return;
pthread_spin_lock(&trace_buf_lock);
PRINT(" (h)___0__1__2__3__4__5__6__7__8__9__A__B__C__D__E__F");
for (z = 0, z1 = 0, i = 0; z < len; z++) {
if (z % 16 == 0) {
if (z != 0) {
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i,
" ");
for (; (z1 < z) && (i < TRACE_BUF_SIZE - 1);
z1++) {
if ((buf[z1] >= 0x20) &&
(buf[z1] < 0x80))
trace_buf[i++] = buf[z1];
else
trace_buf[i++] = '.';
}
trace_buf[i] = '\0';
PRINT("%s", trace_buf);
i = 0;
f = 1;
}
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i,
"%4x: ", z);
}
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%02x ",
buf[z]);
}
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, " ");
for (; (z1 < z) && (i < TRACE_BUF_SIZE - 1); z1++) {
if ((buf[z1] > 0x20) && (buf[z1] < 0x80))
trace_buf[i++] = buf[z1];
else
trace_buf[i++] = '.';
}
trace_buf[i] = '\0';
if (f) {
PRINT("%s", trace_buf)
} else {
PRINT("%s", trace_buf);
}
pthread_spin_unlock(&trace_buf_lock);
return;
}
int debug_init(void)
{
int res;
res = pthread_spin_init(&trace_buf_lock, PTHREAD_PROCESS_PRIVATE);
if (res != 0) {
res = errno;
PRINT_ERROR("pthread_spin_init() failed: %s", strerror(res));
}
return res;
}
void debug_done(void)
{
pthread_spin_destroy(&trace_buf_lock);
}
#endif /* DEBUG || TRACING */
#include "../include/debug.c"
+7 -1
View File
@@ -25,6 +25,7 @@
#include <stdint.h>
#include <getopt.h>
#include <malloc.h>
#include <stdbool.h>
#include <inttypes.h>
#include <signal.h>
#include <sys/types.h>
@@ -36,7 +37,9 @@
char *app_name;
#include "version.h"
#include "common.h"
#include "debug.h"
#if defined(DEBUG) || defined(TRACING)
@@ -62,11 +65,11 @@ char *app_name;
# endif
#endif /* DEBUG */
bool log_daemon = false;
unsigned long trace_flag = DEFAULT_LOG_FLAGS;
#endif /* defined(DEBUG) || defined(TRACING) */
#define DEF_BLOCK_SHIFT 9
#define VERSION_STR "3.1.0-pre1"
#define THREADS 7
#define MAX_VDEVS 10
@@ -391,6 +394,9 @@ int start(int argc, char **argv)
desc.opt.queue_alg = SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER;
desc.opt.qerr = SCST_QERR_0_ALL_RESUME;
desc.opt.d_sense = SCST_D_SENSE_0_FIXED_SENSE;
#ifdef DEBUG_EXT_COPY_REMAP
desc.opt.ext_copy_remap_supported = 1;
#endif
res = ioctl(devs[i].scst_usr_fd, SCST_USER_REGISTER_DEVICE, &desc);
if (res != 0) {
+147
View File
@@ -0,0 +1,147 @@
/*
* debug.c
*
* Copyright (C) 2004 - 2015 Vladislav Bolkhovitin <vst@vlnb.net>
* Copyright (C) 2004 - 2005 Leonid Stoljar
* Copyright (C) 2007 - 2015 SanDisk Corporation
*
* Contains helper functions for execution tracing and error reporting.
* Intended to be included in main .c file.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, version 2
* of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <stdio.h>
#include <time.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
#include "debug.h"
pid_t gettid(void)
{
return syscall(__NR_gettid);
}
#if defined(DEBUG) || defined(TRACING)
#define TRACE_BUF_SIZE 512
static char trace_buf[TRACE_BUF_SIZE];
static pthread_spinlock_t trace_buf_lock;
int debug_print_prefix(unsigned long trace_flag, const char *prefix,
const char *func, int line)
{
int i = 0;
pthread_spin_lock(&trace_buf_lock);
if (trace_flag & TRACE_TIME) {
struct tm t;
time_t tt;
time(&tt);
localtime_r(&tt, &t);
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%d:%d:%d ",
t.tm_hour, t.tm_min, t.tm_sec);
}
if (trace_flag & TRACE_PID)
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "[%d]: ",
gettid());
if (prefix != NULL)
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%s:", prefix);
if (trace_flag & TRACE_FUNCTION)
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%s:", func);
if (trace_flag & TRACE_LINE)
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%i:", line);
if (i > 0)
PRINTN(LOG_INFO, "%s", trace_buf);
pthread_spin_unlock(&trace_buf_lock);
return i;
}
void debug_print_buffer(const void *data, int len)
{
int z, z1, i;
const unsigned char *buf = (const unsigned char *) data;
int f = 0;
if (buf == NULL)
return;
pthread_spin_lock(&trace_buf_lock);
PRINT(LOG_INFO, " (h)___0__1__2__3__4__5__6__7__8__9__A__B__C__D__E__F");
for (z = 0, z1 = 0, i = 0; z < len; z++) {
if (z % 16 == 0) {
if (z != 0) {
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i,
" ");
for (; (z1 < z) && (i < TRACE_BUF_SIZE - 1);
z1++) {
if ((buf[z1] >= 0x20) &&
(buf[z1] < 0x80))
trace_buf[i++] = buf[z1];
else
trace_buf[i++] = '.';
}
trace_buf[i] = '\0';
PRINT(LOG_INFO, "%s", trace_buf);
i = 0;
f = 1;
}
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i,
"%4x: ", z);
}
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, "%02x ",
buf[z]);
}
i += snprintf(&trace_buf[i], TRACE_BUF_SIZE - i, " ");
for (; (z1 < z) && (i < TRACE_BUF_SIZE - 1); z1++) {
if ((buf[z1] > 0x20) && (buf[z1] < 0x80))
trace_buf[i++] = buf[z1];
else
trace_buf[i++] = '.';
}
trace_buf[i] = '\0';
if (f)
PRINT(LOG_INFO, "%s", trace_buf);
else
PRINT(LOG_INFO, "%s", trace_buf);
pthread_spin_unlock(&trace_buf_lock);
}
int debug_init(void)
{
int res;
res = pthread_spin_init(&trace_buf_lock, PTHREAD_PROCESS_PRIVATE);
if (res != 0) {
res = errno;
PRINT_ERROR("pthread_spin_init() failed: %s", strerror(res));
}
return res;
}
void debug_done(void)
{
pthread_spin_destroy(&trace_buf_lock);
}
#endif /* DEBUG || TRACING */
+121 -136
View File
@@ -24,6 +24,8 @@
#include <sys/types.h>
#include <linux/unistd.h>
#include <errno.h>
#include <syslog.h>
#include <stdbool.h>
extern pid_t gettid(void);
@@ -55,8 +57,21 @@ extern pid_t gettid(void);
#define TRACE_ORDER 0x00008000
#define TRACE_ALL 0xffffffff
#define PRINT(format, args...) fprintf(stdout, format "\n", ## args);
#define PRINTN(format, args...) fprintf(stdout, format, ## args);
#define PRINT(priority, format, args...) \
do { \
if (log_daemon) \
syslog(priority, format "\n", ## args); \
else \
fprintf(stdout, format "\n", ## args); \
} while (0)
#define PRINTN(priority, format, args...) \
do { \
if (log_daemon) \
syslog(priority, format, ## args); \
else \
fprintf(stdout, format, ## args); \
} while (0)
extern char *app_name;
#define LOG_PREFIX app_name
@@ -69,6 +84,7 @@ extern char *app_name;
#if defined(DEBUG) || defined(TRACING)
extern bool log_daemon;
extern unsigned long trace_flag;
extern int debug_init(void);
@@ -80,24 +96,23 @@ extern void debug_done(void);
*/
extern int debug_print_prefix(unsigned long trace_flag, const char *prefix,
const char *func, int line);
const char *func, int line);
extern void debug_print_buffer(const void *data, int len);
#define TRACE(trace, format, args...) \
do { \
if (trace_flag & (trace)) \
{ \
debug_print_prefix(trace_flag, __LOG_PREFIX, __FUNCTION__, \
__LINE__); \
PRINT(format, args); \
} \
} while(0)
if (trace_flag & (trace)) { \
debug_print_prefix(trace_flag, __LOG_PREFIX, \
__func__, __LINE__); \
PRINT(LOG_DEBUG, format, args); \
} \
} while (0)
#define PRINT_BUFFER(message, buff, len) \
do { \
PRINT("%s:", message); \
debug_print_buffer(buff, len); \
} while(0)
PRINT(LOG_INFO, "%s:", message); \
debug_print_buffer(buff, len); \
} while (0)
#else /* DEBUG || TRACING */
@@ -115,142 +130,126 @@ static inline void debug_done(void) {}
#define TRACE_MEM(format, args...) \
do { \
if (trace_flag & TRACE_MEMORY) \
{ \
debug_print_prefix(trace_flag, NULL, __FUNCTION__, \
__LINE__); \
PRINT(format, args); \
} \
} while(0)
if (trace_flag & TRACE_MEMORY) { \
debug_print_prefix(trace_flag, NULL, \
__func__, __LINE__); \
PRINT(LOG_DEBUG, format, args); \
} \
} while (0)
#define TRACE_DBG(format, args...) \
do { \
if (trace_flag & TRACE_DEBUG) \
{ \
debug_print_prefix(trace_flag, NULL, __FUNCTION__, \
__LINE__); \
PRINT(format, args); \
} \
} while(0)
if (trace_flag & TRACE_DEBUG) { \
debug_print_prefix(trace_flag, NULL, \
__func__, __LINE__); \
PRINT(LOG_DEBUG, format, args); \
} \
} while (0)
#define TRACE_DBG_SPECIAL(args...) TRACE(TRACE_DEBUG|TRACE_SPECIAL, args)
#define TRACE_MGMT_DBG(format, args...) \
do { \
if (trace_flag & TRACE_MGMT_DEBUG) \
{ \
debug_print_prefix(trace_flag, NULL, __FUNCTION__, \
__LINE__); \
PRINT(format, args); \
} \
} while(0)
if (trace_flag & TRACE_MGMT_DEBUG) { \
debug_print_prefix(trace_flag, NULL, \
__func__, __LINE__); \
PRINT(LOG_DEBUG, format, args); \
} \
} while (0)
#define TRACE_BUFFER(message, buff, len) \
do { \
if (trace_flag & TRACE_BUFF) \
{ \
debug_print_prefix(trace_flag, NULL, __FUNCTION__, \
__LINE__); \
PRINT("%s:", message); \
debug_print_buffer(buff, len); \
} \
} while(0)
if (trace_flag & TRACE_BUFF) { \
debug_print_prefix(trace_flag, NULL, \
__func__, __LINE__); \
PRINT(LOG_DEBUG, "%s:", message); \
debug_print_buffer(buff, len); \
} \
} while (0)
#define TRACE_BUFF_FLAG(flag, message, buff, len) \
do { \
if (trace_flag & (flag)) \
{ \
debug_print_prefix(trace_flag, NULL, __FUNCTION__, \
__LINE__); \
PRINT("%s:", message); \
debug_print_buffer(buff, len); \
} \
} while(0)
if (trace_flag & (flag)) { \
debug_print_prefix(trace_flag, NULL, \
__func__, __LINE__); \
PRINT(LOG_DEBUG, "%s:", message); \
debug_print_buffer(buff, len); \
} \
} while (0)
#define PRINT_INFO(format, args...) \
do { \
debug_print_prefix(trace_flag, __LOG_PREFIX, __FUNCTION__, \
__LINE__); \
PRINT(format, args); \
} while(0)
debug_print_prefix(trace_flag, __LOG_PREFIX, \
__func__, __LINE__); \
PRINT(LOG_INFO, format, args); \
} while (0)
#define PRINT_WARNING(format, args...) \
do { \
debug_print_prefix(trace_flag, __LOG_PREFIX, __FUNCTION__, \
__LINE__); \
PRINT("***WARNING*** " format, args); \
} while(0)
debug_print_prefix(trace_flag, __LOG_PREFIX, \
__func__, __LINE__); \
PRINT(LOG_WARNING, "***WARNING*** " format, args); \
} while (0)
#define PRINT_ERROR(format, args...) \
do { \
debug_print_prefix(trace_flag, __LOG_PREFIX, __FUNCTION__, \
__LINE__); \
PRINT("***ERROR*** " format, args); \
} while(0)
debug_print_prefix(trace_flag, __LOG_PREFIX, \
__func__, __LINE__); \
PRINT(LOG_ERR, "***ERROR*** " format, args); \
} while (0)
#define TRACE_ENTRY() \
do { \
if (trace_flag & TRACE_ENTRYEXIT) \
{ \
if (trace_flag & TRACE_PID) \
{ \
PRINT("[%d]: ENTRY %s", gettid(), \
__FUNCTION__); \
} \
else \
{ \
PRINT("ENTRY %s", __FUNCTION__); \
} \
} \
} while(0)
if (trace_flag & TRACE_ENTRYEXIT) { \
if (trace_flag & TRACE_PID) { \
PRINT(LOG_DEBUG, "[%d]: ENTRY %s", \
gettid(), __func__); \
} else { \
PRINT(LOG_DEBUG, "ENTRY %s", \
__func__); \
} \
} \
} while (0)
#define TRACE_EXIT() \
do { \
if (trace_flag & TRACE_ENTRYEXIT) \
{ \
if (trace_flag & TRACE_PID) \
{ \
PRINT("[%d]: EXIT %s", gettid(), \
__FUNCTION__); \
} \
else \
{ \
PRINT("EXIT %s", __FUNCTION__); \
} \
} \
} while(0)
if (trace_flag & TRACE_ENTRYEXIT) { \
if (trace_flag & TRACE_PID) { \
PRINT(LOG_DEBUG, "[%d]: EXIT %s", \
gettid(), __func__); \
} else { \
PRINT(LOG_DEBUG, "EXIT %s", __func__); \
} \
} \
} while (0)
#define TRACE_EXIT_RES(res) \
do { \
if (trace_flag & TRACE_ENTRYEXIT) \
{ \
if (trace_flag & TRACE_PID) \
{ \
PRINT("[%d]: EXIT %s: %ld", gettid(), \
__FUNCTION__, (long)(res)); \
} \
else \
{ \
PRINT("EXIT %s: %ld", __FUNCTION__, (long)(res)); \
} \
} \
} while(0)
if (trace_flag & TRACE_ENTRYEXIT) { \
if (trace_flag & TRACE_PID) { \
PRINT(LOG_DEBUG, "[%d]: EXIT %s: %ld", \
gettid(), __func__, \
(long)(res)); \
} else { \
PRINT(LOG_DEBUG, "EXIT %s: %ld", \
__func__, (long)(res)); \
} \
} \
} while (0)
#define TRACE_EXIT_HRES(res) \
do { \
if (trace_flag & TRACE_ENTRYEXIT) \
{ \
if (trace_flag & TRACE_PID) \
{ \
PRINT("[%d]: EXIT %s: 0x%lx", gettid(), \
__FUNCTION__, (long)(res)); \
} \
else \
{ \
PRINT("EXIT %s: %lx", __FUNCTION__, (long)(res)); \
} \
} \
} while(0)
if (trace_flag & TRACE_ENTRYEXIT) { \
if (trace_flag & TRACE_PID) { \
PRINT(LOG_DEBUG, "[%d]: EXIT %s: 0x%lx",\
gettid(), __func__, \
(long)(res)); \
} else { \
PRINT(LOG_DEBUG, "EXIT %s: %lx", \
__func__, (long)(res)); \
} \
} \
} while (0)
#else /* DEBUG */
@@ -271,39 +270,25 @@ do { \
#ifdef LOG_PREFIX
#define PRINT_INFO(format, args...) \
do { \
PRINT("%s: " format, LOG_PREFIX, args); \
} while(0)
PRINT(LOG_INFO, "%s: " format, LOG_PREFIX, args) \
#define PRINT_WARNING(format, args...) \
do { \
PRINT("%s: ***WARNING*** " \
format, LOG_PREFIX, args); \
} while(0)
PRINT(LOG_WARNING, "%s: ***WARNING*** " format, \
LOG_PREFIX, args) \
#define PRINT_ERROR(format, args...) \
do { \
PRINT("%s: ***ERROR*** " \
format, LOG_PREFIX, args); \
} while(0)
PRINT(LOG_ERR, "%s: ***ERROR*** " format, \
LOG_PREFIX, args) \
#else
#define PRINT_INFO(format, args...) \
do { \
PRINT(format, args); \
} while(0)
#define PRINT_INFO(format, args...) PRINT(LOG_INFO, format, args)
#define PRINT_WARNING(format, args...) \
do { \
PRINT("***WARNING*** " format, args); \
} while(0)
PRINT(LOG_WARNING, "***WARNING*** " format, args) \
#define PRINT_ERROR(format, args...) \
do { \
PRINT("***ERROR*** " format, args); \
} while(0)
PRINT(LOG_ERR, "***ERROR*** " format, args) \
#endif /* LOG_PREFIX */
+24
View File
@@ -0,0 +1,24 @@
/*
* version.h
*
* Copyright (C) 2015 SanDisk Corporation
*
* Contains macroses for execution tracing and error reporting
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, version 2
* of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __VERSION_H
#define __VERSION_H
#define VERSION_STR "3.2.0-pre1"
#endif /* __VERSION_H */
+104
View File
@@ -0,0 +1,104 @@
ifndef PREFIX
PREFIX=/usr/local
endif
SHELL=/bin/bash
SRCS_F = stpgd_main.c debug.c
OBJS_F = $(SRCS_F:.c=.o)
#SRCS_C =
#OBJS_C = $(SRCS_C:.c=.o)
SCST_INC_DIR := $(shell if [ -e "$$PWD/../../scst" ]; \
then echo "$$PWD/../../scst/include"; \
else echo "$(DESTDIR)$(PREFIX)/include/scst"; fi)
DEBUG_INC_DIR := ../include
INSTALL_DIR := $(DESTDIR)$(PREFIX)/sbin
ON_STPG := $(DESTDIR)$(PREFIX)/bin/scst/scst_on_stpg
CFLAGS += -O2 -Wall -Wextra -Wno-unused-parameter -Wstrict-prototypes \
-I$(SCST_INC_DIR) -I$(DEBUG_INC_DIR) -D_GNU_SOURCE -D__USE_FILE_OFFSET64 \
-D__USE_LARGEFILE64
PROGS = stpgd
LIBS = -lpthread
CFLAGS += -DEXTRACHECKS
#CFLAGS += -DTRACING
CFLAGS += -DDEBUG -g -fno-inline -fno-inline-functions
CFLAGS += -W -Wno-unused-parameter
CFLAGS += $(LOCAL_CFLAGS)
#CFLAGS += -DDEBUG_NOMEM
all: $(PROGS)
stpgd: .depend_f $(OBJS_F)
$(CC) $(OBJS_F) $(LIBS) $(LOCAL_LD_FLAGS) -o $@
#cdrom_tgt: .depend_c $(OBJS_C)
# $(CC) $(OBJS_C) $(LIBS) $(LOCAL_LD_FLAGS) -o $@
ifeq (.depend_f,$(wildcard .depend_f))
-include .depend_f
endif
#ifeq (.depend_c,$(wildcard .depend_c))
#-include .depend_c
#endif
%.o: %.c Makefile
$(CC) -c -o $(@) $(CFLAGS) $(<)
.depend_f:
$(CC) -M $(CFLAGS) $(SRCS_F) >$(@)
#.depend_c:
# $(CC) -M $(CFLAGS) $(SRCS_C) >$(@)
install: all
install -d $(INSTALL_DIR)
install -m 700 $(PROGS) $(INSTALL_DIR)
install -m 700 scst_on_stpg $(ON_STPG)
uninstall:
rm -f $(INSTALL_DIR)/$(PROGS)
rm -rf $(INSTALL_DIR)
clean:
rm -f *.o $(PROGS) .depend*
extraclean: clean
rm -f *.orig *.rej
2release:
sed -i.aa s/"^C\?FLAGS += \-DEXTRACHECKS"/"#CFLAGS += \-DEXTRACHECKS"/ Makefile
grep "^#CFLAGS += \-DEXTRACHECKS" Makefile >/dev/null
sed -i.aa s/"^#\?CFLAGS += \-DTRACING"/"CFLAGS += \-DTRACING"/ Makefile
grep "^CFLAGS += \-DTRACING" Makefile >/dev/null
sed -i.aa s/"^C\?FLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/"#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/ Makefile
grep "^#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions" Makefile >/dev/null
rm Makefile.aa
2debug:
sed -i.aa s/"^#\?CFLAGS += \-DEXTRACHECKS"/"CFLAGS += \-DEXTRACHECKS"/ Makefile
grep "^CFLAGS += \-DEXTRACHECKS" Makefile >/dev/null
sed -i.aa s/"^C\?FLAGS += \-DTRACING"/"#CFLAGS += \-DTRACING"/ Makefile
grep "^#CFLAGS += \-DTRACING" Makefile >/dev/null
sed -i.aa s/"^#\?CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/"CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/ Makefile
grep "^CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions" Makefile >/dev/null
rm Makefile.aa
2perf:
sed -i.aa s/"^C\?FLAGS += \-DEXTRACHECKS"/"#CFLAGS += \-DEXTRACHECKS"/ Makefile
grep "^#CFLAGS += \-DEXTRACHECKS" Makefile >/dev/null
sed -i.aa s/"^C\?FLAGS += \-DTRACING"/"#CFLAGS += \-DTRACING"/ Makefile
grep "^#CFLAGS += \-DTRACING" Makefile >/dev/null
sed -i.aa s/"^C\?FLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/"#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions"/ Makefile
grep "^#CFLAGS += \-DDEBUG -g -fno-inline -fno-inline-functions" Makefile >/dev/null
rm Makefile.aa
release-archive:
../../scripts/generate-release-archive stpgd "$$(sed -n 's/^#define[[:blank:]]VERSION_STR[[:blank:]]*\"\([^\"]*\)\".*/\1/p' stpgd_main.c)"
.PHONY: all install uninstall clean extraclean 2release 2debug 2perf
+9
View File
@@ -0,0 +1,9 @@
This is stpgd service called by the SCST core upon receiving SET TARGET
PORT GROUPS command via SCST events subsystem. In turn, stpgd calls
external script to actually modify ALUA state as requested by the SET
TARGET PORT GROUPS command via cluster manager and SCST sysfs. You can
find example of such script in scst_on_stpg file.
Reason why such dual stage approach is used is, because there is no way
from inside the kernel to control execution of external programs and
there is no way to write a service calling IOCTLs on shell.
+1
View File
@@ -0,0 +1 @@
#include "../include/debug.c"
+11
View File
@@ -0,0 +1,11 @@
#!/bin/bash
#
# Script invoked by SCST for processing a SCSI SET TARGET PORT GROUPS command
#
# Arguments: SCST_DEVICE_NAME (device name where this STPG command was
# received on), SCST_PREV_ALUA_STATE, SCST_ALUA_STATE,
# SCST_DEVICE_GROUP and SCST_TARGET_GROUP + environmental variables
# with the same names set
#
echo $SCST_ALUA_STATE >/sys/kernel/scst_tgt/device_groups/$SCST_DEVICE_GROUP/target_groups/$SCST_TARGET_GROUP/state
+562
View File
@@ -0,0 +1,562 @@
/*
* stpgd_main.c
*/
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <malloc.h>
#include <inttypes.h>
#include <sys/wait.h>
#include <sys/poll.h>
#include <sys/ioctl.h>
#include <stdbool.h>
#include <pthread.h>
#include <unistd.h>
#include <syslog.h>
#include <signal.h>
#include <syslog.h>
#include "version.h"
#include "debug.h"
#include "scst_event.h"
char *app_name;
#define DEFAULT_TRANSITION_TIME 17
#if defined(DEBUG) || defined(TRACING)
#ifdef DEBUG
#define DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MINOR | TRACE_PID | \
TRACE_FUNCTION | TRACE_SPECIAL | TRACE_MGMT | TRACE_MGMT_DEBUG | \
TRACE_TIME)
#define TRACE_SN(args...) TRACE(TRACE_SCSI_SERIALIZING, args)
#else /* DEBUG */
# ifdef TRACING
#define DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MGMT | \
TRACE_TIME | TRACE_SPECIAL)
# else
#define DEFAULT_LOG_FLAGS 0
# endif
#endif /* DEBUG */
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
bool log_daemon = true;
unsigned long trace_flag = DEFAULT_LOG_FLAGS;
#endif /* defined(DEBUG) || defined(TRACING) */
int transition_timeout = DEFAULT_TRANSITION_TIME;
static struct option const long_options[] = {
{"path", required_argument, 0, 'p'},
{"timeout", required_argument, 0, 't'},
{"foreground", no_argument, 0, 'f'},
#if defined(DEBUG) || defined(TRACING)
{"debug", required_argument, 0, 'd'},
#endif
{"version", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0},
};
int stpg_init_report_pipe[2];
char *stpg_path;
static void usage(int status)
{
if (status != 0)
fprintf(stderr, "Try '%s --help' for more information.\n", app_name);
else {
printf("Usage: %s [OPTIONS]\n", app_name);
printf("STPG target daemon\n");
printf(" -f, --foreground make the program run in the foreground\n");
printf(" -p, --path absolute path to the STPG script\n");
printf(" -t, --timeout transition timeout\n");
#if defined(DEBUG) || defined(TRACING)
printf(" -d, --debug=level debug tracing level\n");
#endif
printf(" -h, --help display this help and exit\n");
}
}
static void stpg_handle_tm_received(struct scst_event_user *event_user)
{
/*
* Put code to abort state transition here, if this STPG cmd,
* identified by cmd_to_abort_tag or RESET, requested to be aborted
*/
}
int invoke_stpg(const uint8_t *device_name,
const struct scst_event_stpg_descr *descr, pid_t *out_pid)
{
char *args[7], *env[7];
int res = 0, ret, i;
pid_t c_pid;
args[0] = stpg_path;
args[1] = (char *)device_name;
args[2] = (char *)descr->prev_state;
args[3] = (char *)descr->new_state;
args[4] = (char *)descr->dg_name;
args[5] = (char *)descr->tg_name;
args[6] = NULL;
env[0] = "PATH=/bin:/usr/bin:/sbin:/usr/sbin";
ret = asprintf(&env[1], "SCST_DEVICE_NAME=%s", device_name);
if (ret < 0) {
res = -errno;
PRINT_ERROR("asprintf() failed: %d (%s)", res, strerror(-res));
goto out;
}
ret = asprintf(&env[2], "SCST_PREV_ALUA_STATE=%s", descr->prev_state);
if (ret < 0) {
res = -errno;
PRINT_ERROR("asprintf() failed: %d (%s)", res, strerror(-res));
goto out;
}
ret = asprintf(&env[3], "SCST_ALUA_STATE=%s", descr->new_state);
if (ret < 0) {
res = -errno;
PRINT_ERROR("asprintf() failed: %d (%s)", res, strerror(-res));
goto out;
}
ret = asprintf(&env[4], "SCST_DEVICE_GROUP=%s", descr->dg_name);
if (ret < 0) {
res = -errno;
PRINT_ERROR("asprintf() failed: %d (%s)", res, strerror(-res));
goto out;
}
ret = asprintf(&env[5], "SCST_TARGET_GROUP=%s", descr->tg_name);
if (ret < 0) {
res = -errno;
PRINT_ERROR("asprintf() failed: %d (%s)", res, strerror(-res));
goto out;
}
env[6] = NULL;
PRINT_INFO("Invoking script %s with parameters: %s %s %s %s %s and environment: "
"%s %s %s %s %s", stpg_path, args[1], args[2], args[3],
args[4], args[5], env[1], env[2], env[3], env[4], env[5]);
c_pid = fork();
if (c_pid == 0) {
ret = setpgid(getpid(), getpid());
if (ret < 0) {
res = -errno;
PRINT_ERROR("setgid failed %d (%s)", ret, strerror(-ret));
}
TRACE_DBG("pgid %d (pid %d)", getpgid(getpid()), getpid());
ret = execve(stpg_path, args, env);
if (ret < 0) {
res = -errno;
PRINT_ERROR("EXEC failed %d (%s)", ret, strerror(-ret));
}
exit(0);
} else if (c_pid < 0) {
res = -errno;
PRINT_ERROR("fork() failed: %d (%s)", res, strerror(-res));
}
*out_pid = c_pid;
for (i = 1; i < (signed)ARRAY_SIZE(env); i++)
free(env[i]);
out:
return res;
}
/* Returns 0, if the pid is still running, >0 if it was exited or <0 error code */
int wait_until_finished(pid_t pid, unsigned long deadline, int *status, int child)
{
int res;
time_t start, end;
double elapsed;
TRACE_ENTRY();
time(&start);
do {
res = waitpid(pid, status, WNOHANG);
if (res != 0) {
if (res < 0) {
res = -errno;
PRINT_ERROR("Waitpid for pid %d (child %d) "
"failed: %d (%s)", pid, child,
errno, strerror(errno));
}
break;
}
sleep(0.1);
time(&end);
elapsed = difftime(end, start);
} while (elapsed < deadline);
TRACE_EXIT_RES(res);
return res;
}
int handle_stpg_received(struct scst_event_user *event_user)
{
const struct scst_event_stpg_payload *p = (struct scst_event_stpg_payload *)event_user->out_event.payload;
int num, k;
int res = 0;
pid_t pids[p->stpg_descriptors_cnt];
TRACE_DBG("device name %s, stpg_descriptors_cnt %d", p->device_name,
p->stpg_descriptors_cnt);
for (num = 0; num < p->stpg_descriptors_cnt; num++) {
res = invoke_stpg(p->device_name, &p->stpg_descriptors[num], &pids[num]);
TRACE_DBG("num %d, res %d, pid %d", num, res, pids[num]);
if (res != 0)
break;
}
TRACE_DBG("num %d", num);
for (k = 0; k < num; k++) {
int status = 0, rc;
TRACE_DBG("k %d, pid %d", k, pids[k]);
rc = wait_until_finished(pids[k], transition_timeout, &status, k);
TRACE_DBG("rc %d, status %d", rc, WEXITSTATUS(status));
if (rc > 0) {
if (res == 0)
res = WEXITSTATUS(status);
continue;
} else if (rc < 0) {
if (res == 0)
res = rc;
continue;
}
PRINT_WARNING("on_stpg %d (pid %d) did not finish on time - "
"sending SIGTERM", k, pids[k]);
if (res == 0)
res = -ETIMEDOUT;
rc = killpg(pids[k], SIGTERM);
if (rc < 0)
PRINT_ERROR("Failed to send SIGTERM to child %d (pid %d): %d/%s",
k, pids[k], errno, strerror(errno));
rc = wait_until_finished(pids[k], 1, &status, k);
if (rc != 0)
continue;
while (1) {
PRINT_WARNING("on_stpg %d (pid %d) did not finish on time - "
"sending SIGKILL", k, pids[k]);
rc = killpg(pids[k], SIGKILL);
if (rc < 0) {
PRINT_ERROR("Failed to send SIGKILL to child %d "
"(pid %d): %d/%s", k, pids[k], errno,
strerror(errno));
break;
}
rc = wait_until_finished(pids[k], 1, &status, k);
if (rc != 0)
break;
};
}
TRACE_EXIT_RES(res);
return res;
}
static int stpg_event_loop(void)
{
int res = 0, status;
int event_fd;
uint8_t event_user_buf[1024*1024];
pid_t c_pid = 0;
struct pollfd pl;
struct scst_event_user *event_user =
(struct scst_event_user *)event_user_buf;
struct scst_event e1;
bool first_error = true;
event_fd = open(SCST_EVENT_DEV, O_RDWR);
if (event_fd < 0) {
res = -errno;
PRINT_ERROR("Unable to open SCST event device %s (%s)",
SCST_EVENT_DEV, strerror(-res));
goto out;
}
close(stpg_init_report_pipe[0]);
if (log_daemon)
res = write(stpg_init_report_pipe[1], &res, sizeof(res));
close(stpg_init_report_pipe[1]);
memset(&pl, 0, sizeof(pl));
pl.fd = event_fd;
pl.events = POLLIN;
memset(&e1, 0, sizeof(e1));
e1.event_code = SCST_EVENT_STPG_USER_INVOKE;
strncpy(e1.issuer_name, SCST_EVENT_SCST_CORE_ISSUER,
sizeof(e1.issuer_name));
e1.issuer_name[sizeof(e1.issuer_name)-1] = '\0';
PRINT_INFO("Setting allowed event code %d, issuer_name %s",
e1.event_code, e1.issuer_name);
res = ioctl(event_fd, SCST_EVENT_ALLOW_EVENT, &e1);
if (res != 0) {
res = -errno;
PRINT_ERROR("SCST_EVENT_ALLOW_EVENT failed: %d (%s)",
res, strerror(-res));
goto out;
}
e1.event_code = SCST_EVENT_TM_FN_RECEIVED;
strncpy(e1.issuer_name, SCST_EVENT_SCST_CORE_ISSUER,
sizeof(e1.issuer_name));
e1.issuer_name[sizeof(e1.issuer_name)-1] = '\0';
PRINT_INFO("Setting allowed event code %d, issuer_name %s",
e1.event_code, e1.issuer_name);
res = ioctl(event_fd, SCST_EVENT_ALLOW_EVENT, &e1);
if (res != 0) {
res = -errno;
PRINT_ERROR("SCST_EVENT_ALLOW_EVENT failed: %d (%s)",
res, strerror(-res));
goto out;
}
while (1) {
memset(event_user_buf, 0, sizeof(event_user_buf));
event_user->max_event_size = sizeof(event_user_buf);
res = ioctl(event_fd, SCST_EVENT_GET_NEXT_EVENT, event_user);
if (res != 0) {
res = -errno;
switch (-res) {
case ESRCH:
case EBUSY:
TRACE_MGMT_DBG("SCST_EVENT_GET_NEXT_EVENT "
"returned %d (%s)", res, strerror(res));
/* go through */
case EINTR:
continue;
case EAGAIN:
TRACE_DBG("SCST_EVENT_GET_NEXT_EVENT, "
"returned EAGAIN (%d)", -res);
continue;
default:
PRINT_ERROR("SCST_EVENT_GET_NEXT_EVENT "
"failed: %d (%s)", res, strerror(-res));
if (!first_error)
goto out;
first_error = false;
continue;
}
first_error = true;
again_poll:
res = poll(&pl, 1, c_pid > 0 ? 1 : 0);
if (res > 0)
continue;
else if (res == 0)
goto again_poll;
else {
res = -errno;
switch (res) {
case ESRCH:
case EBUSY:
case EAGAIN:
TRACE_MGMT_DBG("poll() returned %d "
"(%s)", res, strerror(-res));
case EINTR:
goto again_poll;
default:
PRINT_ERROR("poll() failed: %d (%s)",
res, strerror(-res));
goto again_poll;
}
}
}
first_error = true;
#ifdef DEBUG
PRINT_INFO("event_code %d, issuer_name %s",
event_user->out_event.event_code,
event_user->out_event.issuer_name);
#endif
if (event_user->out_event.payload_len != 0)
TRACE_BUFFER("payload", event_user->out_event.payload,
event_user->out_event.payload_len);
if (event_user->out_event.event_code == SCST_EVENT_STPG_USER_INVOKE) {
c_pid = fork();
if (c_pid == -1)
PRINT_ERROR("Failed to fork: %d", c_pid);
else if (c_pid == 0) {
struct scst_event_notify_done d;
signal(SIGCHLD, SIG_DFL);
status = handle_stpg_received(event_user);
memset(&d, 0, sizeof(d));
d.event_id = event_user->out_event.event_id;
d.status = status;
res = ioctl(event_fd, SCST_EVENT_NOTIFY_DONE, &d);
if (res != 0) {
res = -errno;
PRINT_ERROR("SCST_EVENT_NOTIFY_DONE "
"failed: %s (res %d)",
strerror(-res), res);
} else
PRINT_INFO("STPG event completed with status %d", status);
exit(res);
}
} else if (event_user->out_event.event_code == SCST_EVENT_TM_FN_RECEIVED)
stpg_handle_tm_received(event_user);
else
PRINT_ERROR("Unknown event %d received", event_user->out_event.event_code);
}
out:
return res;
}
void sig_chld(int signal)
{
/* Check just in case */
if (signal == SIGCHLD) {
TRACE_DBG("Cleanup zombie (pid %d)", getpid());
wait(NULL);
}
}
int main(int argc, char **argv)
{
int res = 0, ch, longindex;
pid_t pid;
struct sigaction sa;
setlinebuf(stdout);
openlog(argv[0], LOG_PID, LOG_USER);
res = pipe(stpg_init_report_pipe);
if (res == -1) {
res = -errno;
PRINT_ERROR("Pipe failed: %d (%s)", res, strerror(-res));
goto out;
}
sa.sa_handler = &sig_chld;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
if (sigaction(SIGCHLD, &sa, 0) == -1) {
PRINT_ERROR("sigaction() failed: %d/%s", errno, strerror(errno));
exit(1);
}
/*
* Otherwise we could die in some later write() during the event_loop()
* instead of getting EPIPE!
*/
signal(SIGPIPE, SIG_IGN);
res = debug_init();
if (res != 0)
goto out;
app_name = argv[0];
while ((ch = getopt_long(argc, argv, "+d:fp:t:hv",
long_options, &longindex)) >= 0) {
switch (ch) {
case 'p':
stpg_path = optarg;
break;
#if defined(DEBUG) || defined(TRACING)
case 'd':
trace_flag = strtol(optarg, (char **)NULL, 0);
break;
#endif
case 'v':
printf("%s version %s\n", app_name, VERSION_STR);
goto out_done;
case 'f':
log_daemon = false;
break;
case 't':
transition_timeout = strtol(optarg, (char **)NULL, 0);
if (transition_timeout < 0) {
printf("Invalid timeout %d\n", transition_timeout);
res = -EINVAL;
goto out_done;
}
break;
case 'h':
usage(0);
goto out_done;
default:
goto out_usage;
}
}
if (!stpg_path)
stpg_path = "/usr/local/bin/scst/scst_on_stpg";
if (access(stpg_path, X_OK) == -1) {
PRINT_ERROR("Script file \" %s \"does not exist or not "
"executable", stpg_path);
res = -1;
goto out_done;
}
#ifdef DEBUG
PRINT_INFO("trace_flag %lx", trace_flag);
#endif
if (log_daemon) {
trace_flag &= ~TRACE_TIME;
trace_flag &= ~TRACE_PID;
pid = fork();
if (pid < 0) {
PRINT_ERROR("starting daemon failed(%d)", pid);
res = pid;
goto out_done;
} else if (pid) {
int res1 = -1;
close(stpg_init_report_pipe[1]);
if ((unsigned)read(stpg_init_report_pipe[0], &res1, sizeof(res1)) < sizeof(res1)) {
res = -1;
goto out_done;
} else {
res = res1;
goto out_done;
}
}
close(0);
open("/dev/null", O_RDWR);
dup2(0, 1);
dup2(0, 2);
setsid();
}
res = stpg_event_loop();
out_done:
debug_done();
out:
closelog();
return res;
out_usage:
usage(1);
goto out_done;
}