/*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. By downloading, copying, installing or
* using the software you agree to this license. If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Intel License Agreement
*
* Copyright (c) 2000, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that
* the following conditions are met:
*
* -Redistributions of source code must retain the above copyright notice, this list of conditions and the
* following disclaimer.
*
* -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other materials provided with the distribution.
*
* -The name of Intel Corporation may not be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Intel SCSI device driver for iSCSI
*/
#ifdef __linux__
#include <linux/blk.h>
#include <linux/string.h>
#include <scsi.h>
#include <hosts.h>
#include <sd.h>
#endif
#include "iscsiutil.h"
#include "util.c"
#include "driver.h"
#include "iscsi.h"
#include "iscsi.c"
#include "tests.h"
#include "tests.c"
#include "osd_ops.h"
#include "osd_ops.c"
#include "parameters.h"
#include "parameters.c"
#include "initiator.h"
#include "initiator.c"
/*
* Version-specific include files
*/
#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
#include <linux/in.h>
#else
struct proc_dir_entry iscsi_proc_dir = {PROC_SCSI_NOT_PRESENT, 5, "iscsi", S_IFDIR | S_IRUGO | S_IXUGO, 2};
#endif
#ifdef MODULE
Scsi_Host_Template driver_template = ISCSI;
#include "scsi_module.c"
#endif
/*
* Globals
*/
static initiator_cmd_t *g_cmd;
static iscsi_queue_t g_cmd_q;
static struct iovec **g_iov;
static iscsi_queue_t g_iovec_q;
static iscsi_driver_stats_t g_stats;
/*
* Definitions
*/
/*
* Starting with kernel 2.4.10, we define a license string. This source is under BSD License.
* Consult <linux/include/linux/module.h> for details
*/
MODULE_AUTHOR("Intel Corporation, <http://www.intel.com>");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,10)
MODULE_LICENSE("Dual BSD/GPL"); /* This source is under BSD License. This is the closest ident that module.h provides */
# endif
/*
* Private
*/
static int driver_init(void) {
int i;
iscsi_trace(TRACE_SCSI_DEBUG, __FILE__, __LINE__, "initializing iSCSI driver\n");
if ((g_cmd=iscsi_malloc_atomic(sizeof(initiator_cmd_t)*CONFIG_INITIATOR_QUEUE_DEPTH))==NULL) {
iscsi_trace_error("iscsi_malloc_atomic() failed\n");
return -1;
}
if ((g_iov=iscsi_malloc_atomic(sizeof(struct iovec*)*CONFIG_INITIATOR_QUEUE_DEPTH))==NULL) {
iscsi_trace_error("iscsi_malloc_atomic() failed\n");
iscsi_free_atomic(g_cmd);
return -1;
}
for (i=0; i<CONFIG_INITIATOR_QUEUE_DEPTH; i++) {
g_iov[i] = NULL;
g_cmd[i].ptr = NULL;
}
#define DI_ERROR { \
for (i=0; i<CONFIG_INITIATOR_QUEUE_DEPTH; i++) { \
if (g_cmd[i].ptr != NULL) iscsi_free_atomic(g_cmd[i].ptr); \
} \
for (i=0; i<CONFIG_INITIATOR_QUEUE_DEPTH; i++) { \
if (g_iov[i] != NULL) iscsi_free_atomic(g_iov[i]); \
} \
iscsi_free_atomic(g_iov); \
iscsi_free_atomic(g_cmd); \
return -1; \
}
for (i=0; i<CONFIG_INITIATOR_QUEUE_DEPTH; i++) {
if ((g_iov[i]=iscsi_malloc_atomic(sizeof(struct iovec)*SG_ALL))==NULL) {
iscsi_trace_error("iscsi_malloc_atomic() failed\n");
DI_ERROR;
}
}
if (iscsi_queue_init(&g_cmd_q, CONFIG_INITIATOR_QUEUE_DEPTH)!=0) {
iscsi_trace_error("iscsi_queue_init() failed\n");
DI_ERROR;
}
if (iscsi_queue_init(&g_iovec_q, CONFIG_INITIATOR_QUEUE_DEPTH)!=0) {
iscsi_trace_error("iscsi_queue_init() failed\n");
DI_ERROR;
}
for (i=0; i<CONFIG_INITIATOR_QUEUE_DEPTH; i++) {
if ((g_cmd[i].ptr = iscsi_malloc_atomic(sizeof(iscsi_scsi_cmd_args_t)))==NULL) {
iscsi_trace_error("iscsi_malloc_atomic() failed\n");
DI_ERROR;
}
g_cmd[i].type = ISCSI_SCSI_CMD;
if (iscsi_queue_insert(&g_cmd_q, &g_cmd[i])!=0) {
iscsi_trace_error("iscsi_queue_insert() failed\n");
DI_ERROR;
}
if (iscsi_queue_insert(&g_iovec_q, g_iov[i])!=0) {
iscsi_trace_error("iscsi_queue_insert() failed\n");
DI_ERROR;
}
}
memset(&g_stats, 0, sizeof(g_stats));
iscsi_spin_init(&g_stats.lock);
if (initiator_init()!=0) {
iscsi_trace_error("initiator_init() failed\n");
DI_ERROR;
}
iscsi_trace(TRACE_SCSI_DEBUG, "iSCSI initialization complete\n");
return 0;
}
static int driver_shutdown(void) {
int i;
iscsi_trace(TRACE_SCSI_DEBUG, "shutting down iSCSI driver\n");
if (initiator_shutdown()!=0) {
iscsi_trace_error("initiator_shutdown() failed\n");
return -1;
}
iscsi_spin_destroy(&g_stats.lock);
iscsi_queue_destroy(&g_iovec_q);
iscsi_queue_destroy(&g_cmd_q);
for (i=0; i<CONFIG_INITIATOR_QUEUE_DEPTH; i++) {
iscsi_free_atomic(g_iov[i]);
iscsi_free_atomic(g_cmd[i].ptr);
}
iscsi_free_atomic(g_cmd);
iscsi_free_atomic(g_iov);
iscsi_trace(TRACE_SCSI_DEBUG, "iSCSI driver shutdown complete\n");
return 0;
}
/*
* Public
*/
int iscsi_detect(Scsi_Host_Template *tptr) {
struct Scsi_Host *ptr;
iscsi_trace(TRACE_SCSI_DEBUG, "detecting iSCSI host\n");
spin_unlock(&io_request_lock);
if (driver_init()!=0) {
iscsi_trace_error("driver_init() failed\n");
spin_lock(&io_request_lock);
return 0; /*No 'SCSI' host detected, return 0 */
}
ptr = scsi_register(tptr, 0);
ptr->max_id = CONFIG_INITIATOR_NUM_TARGETS;
ptr->max_lun = CONFIG_DRIVER_MAX_LUNS;
ptr->max_cmd_len = 255;
iscsi_trace(TRACE_SCSI_DEBUG, "iSCSI host detected\n");
spin_lock(&io_request_lock);
return 1;
}
int iscsi_release(struct Scsi_Host *host) {
iscsi_trace(TRACE_SCSI_DEBUG, "releasing iSCSI host\n");
driver_shutdown();
iscsi_trace(TRACE_SCSI_DEBUG, "iSCSI host released\n");
return 0;
}
int iscsi_bios_param(Disk *disk, kdev_t dev, int *ip) {
ip[0] = 32; /* heads */
ip[1] = 63; /* sectors */
if((ip[2] = disk->capacity >> 11) > 1024) { /* cylinders, test for big disk */
ip[0] = 255; /* heads */
ip[1] = 63; /* sectors */
ip[2] = disk->capacity / (255 * 63); /* cylinders */
}
iscsi_trace(TRACE_SCSI_DEBUG, "%u sectors, H/S/C: %u/%u/%u\n", disk->capacity, ip[0], ip[1], ip[2]);
return 0;
}
int iscsi_command(Scsi_Cmnd *SCpnt) {
iscsi_trace(TRACE_SCSI_DEBUG, "0x%p: op 0x%x, chan %i, target %i, lun %i, bufflen %i, sg %i\n",
SCpnt, SCpnt->cmnd[0], SCpnt->channel, SCpnt->target, SCpnt->lun,
SCpnt->request_bufflen, SCpnt->use_sg);
iscsi_trace_error("NOT IMPLEMENTED\n");
return -1;
}
int iscsi_done(void *ptr) {
initiator_cmd_t *cmd = (initiator_cmd_t *) ptr;
iscsi_scsi_cmd_args_t *scsi_cmd = (iscsi_scsi_cmd_args_t *) cmd->ptr;
Scsi_Cmnd *SCpnt = (Scsi_Cmnd *) cmd->callback_arg;
unsigned long flags = 0;
if (SCpnt==0) {
return 0;
}
if (cmd->status==0) {
SCpnt->result = scsi_cmd->status;
} else {
SCpnt->result = -1;
}
iscsi_trace(TRACE_SCSI_DEBUG, "scsi_arg 0x%p SCpnt 0x%p op 0x%x done (result %i)\n",
scsi_cmd, SCpnt, SCpnt->cmnd[0], SCpnt->result);
if ((scsi_cmd->input)&&(scsi_cmd->output)) {
iscsi_trace_error("bidi xfers not implemented\n");
return -1;
} else if (scsi_cmd->input) {
iscsi_spin_lock_irqsave(&g_stats.lock, &flags);
if ((g_stats.rx+SCpnt->request_bufflen)<g_stats.rx) {
g_stats.rx_overflow++;
}
g_stats.rx += SCpnt->request_bufflen;
g_stats.rx_queued -= SCpnt->request_bufflen;
if (g_stats.num_rx_queued) g_stats.num_rx_queued--;
g_stats.num_rx++;
iscsi_spin_unlock_irqrestore(&g_stats.lock, &flags);
} else if (scsi_cmd->output) {
iscsi_spin_lock_irqsave(&g_stats.lock, &flags);
if ((g_stats.tx+SCpnt->request_bufflen)<g_stats.tx) {
g_stats.tx_overflow++;
}
g_stats.tx += SCpnt->request_bufflen;
g_stats.tx_queued -= SCpnt->request_bufflen;
if (g_stats.num_tx_queued) g_stats.num_tx_queued--;
g_stats.num_tx++;
iscsi_spin_unlock_irqrestore(&g_stats.lock, &flags);
}
if (SCpnt->host_scribble) {
unsigned char *hs;
hs = SCpnt->host_scribble;
SCpnt->host_scribble = NULL; /* for abort */
if (iscsi_queue_insert(&g_iovec_q, hs)!=0) {
iscsi_trace_error("iscsi_queue_insert() failed\n");
return -1;
}
}
iscsi_free_atomic(scsi_cmd->ahs);
if (iscsi_queue_insert(&g_cmd_q, cmd)!=0) {
iscsi_trace_error("iscsi_queue_insert() failed\n");
cmd->callback_arg = NULL; /* for abort */
return -1;
}
cmd->callback_arg = NULL; /* for abort */
if (SCpnt->result==0) {
SCpnt->scsi_done(SCpnt);
} else {
iscsi_trace_error("SCSI cmd 0x%x failed at iSCSI level (ignoring)\n", SCpnt->cmnd[0]);
}
return 0;
}
int iscsi_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
initiator_cmd_t *cmd;
iscsi_scsi_cmd_args_t *scsi_cmd;
unsigned char *data;
unsigned length, trans_len;
int input, output;
unsigned long flags =0;
/* Tagged command queuing is handled from within this SCSI driver. */
if ((SCpnt->device->tagged_supported)&&(SCpnt->device->tagged_queue)) {
SCpnt->tag = SCpnt->device->current_tag++;
}
spin_unlock(&io_request_lock);
iscsi_trace(TRACE_SCSI_DEBUG, "SCpnt %p: tid %i lun %i op 0x%x tag %u len %i sg %i buff 0x%p\n",
SCpnt, SCpnt->target, SCpnt->lun, SCpnt->cmnd[0], SCpnt->tag, SCpnt->request_bufflen,
SCpnt->use_sg, SCpnt->buffer);
/* Determine direction of data transfer */
trans_len = length = output = input = 0;
if ((SCpnt->cmnd[0]!=TEST_UNIT_READY)&&(SCpnt->request_bufflen)) {
if (SCpnt->sc_data_direction == SCSI_DATA_WRITE) {
output = 1; input = 0;
length = trans_len = SCpnt->request_bufflen;
iscsi_spin_lock_irqsave(&g_stats.lock, &flags);
g_stats.num_tx_queued++;
g_stats.tx_queued += trans_len;
iscsi_spin_unlock_irqrestore(&g_stats.lock, &flags);
} else if (SCpnt->sc_data_direction == SCSI_DATA_READ) {
length = output = 0; input = 1;
trans_len = SCpnt->request_bufflen;
iscsi_spin_lock_irqsave(&g_stats.lock, &flags);
g_stats.num_rx_queued++;
g_stats.rx_queued += trans_len;
iscsi_spin_unlock_irqrestore(&g_stats.lock, &flags);
}
}
/* Convert scatterlist to iovec */
if (SCpnt->use_sg) {
struct scatterlist *sg = (struct scatterlist *) SCpnt->buffer;
struct iovec *iov;
int i;
iov = iscsi_queue_remove(&g_iovec_q);
if (iov == NULL) {
iscsi_trace_error("iscsi_queue_remove() failed\n");
spin_lock(&io_request_lock);
return -1;
}
for (i=0; i<SCpnt->use_sg; i++) {
iov[i].iov_base = sg[i].address;
iov[i].iov_len = sg[i].length;
}
data = SCpnt->host_scribble = (unsigned char *)iov;
} else {
data = SCpnt->buffer;
SCpnt->host_scribble = NULL;
}
/* Get free cmd structure */
if ((cmd=iscsi_queue_remove(&g_cmd_q))==NULL) {
iscsi_trace_error("iscsi_queue_remove() failed\n");
spin_lock(&io_request_lock);
return -1;
}
scsi_cmd = (iscsi_scsi_cmd_args_t *) cmd->ptr;
memset(scsi_cmd, 0, sizeof(*scsi_cmd));
scsi_cmd->send_data = output?data:0;
scsi_cmd->send_sg_len = output?SCpnt->use_sg:0;
scsi_cmd->recv_data = input?data:NULL;
scsi_cmd->recv_sg_len = input?SCpnt->use_sg:0;
scsi_cmd->input = input;
scsi_cmd->output = output;
scsi_cmd->length = length;
scsi_cmd->lun = SCpnt->lun;
scsi_cmd->lun = scsi_cmd->lun << 32;
scsi_cmd->trans_len = trans_len;
scsi_cmd->cdb = SCpnt->cmnd;
/* AHS for CDBs larget than 16 bytes */
if (SCpnt->cmd_len>16) {
iscsi_trace(TRACE_ISCSI_DEBUG, "creating AHS for extended CDB (%i bytes)\n", SCpnt->cmd_len);
if ((scsi_cmd->ahs=iscsi_malloc_atomic(SCpnt->cmd_len-16))==NULL) {
iscsi_trace_error("iscsi_malloc_atomic() failed\n");
spin_lock(&io_request_lock);
return -1;
}
memset(scsi_cmd->ahs, 0, 4);
*((uint16_t *)scsi_cmd->ahs) = HTONS(SCpnt->cmd_len-15); /* AHS length */
scsi_cmd->ahs[2] = 0x01; /* Type */
memcpy(scsi_cmd->ahs+4, SCpnt->cmnd+16, SCpnt->cmd_len-16); /* Copy in remaining CDB */
scsi_cmd->ahs_len = SCpnt->cmd_len-16;
}
SCpnt->scsi_done = done; /* The midlayer's callback called in iscsi_done */
SCpnt->result = 0x02; /* Default to a check condition */
cmd->callback = iscsi_done; /* This driver's callback called by initiator library */
cmd->callback_arg = SCpnt;
cmd->isid = SCpnt->target;
if (initiator_enqueue(cmd)!=0) {
iscsi_trace_error("initiator_enqueue() failed\n");
if (SCpnt->cmd_len>16) iscsi_free_atomic(scsi_cmd->ahs);
spin_lock(&io_request_lock);
return -1;
}
spin_lock(&io_request_lock);
return 0;
}
int iscsi_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int writing) {
unsigned char *info = NULL;
uint32_t infolen = 8192;
int len = 0;
iscsi_trace(TRACE_SCSI_DEBUG, "buffer = 0x%p, offset %u, length = %i, hostno %i, writing %i\n",
buffer, (unsigned) offset, length, hostno, writing);
/* writing resets counters */
if (writing) {
iscsi_spin_lock(&g_stats.lock);
g_stats.num_tx = g_stats.num_tx_queued = 0;
g_stats.num_rx = g_stats.num_rx_queued = 0;
g_stats.tx_queued = g_stats.tx = g_stats.tx_overflow = g_stats.tx_error = 0;
g_stats.rx_queued = g_stats.rx = g_stats.rx_overflow = g_stats.rx_error = 0;
g_stats.aborts_success = g_stats.aborts_failed = 0;
g_stats.device_resets = g_stats.bus_resets = g_stats.host_resets = 0;
iscsi_spin_unlock(&g_stats.lock);
return 0;
} else {
if ((info=iscsi_malloc_atomic(infolen))==NULL) {
iscsi_trace_error("iscsi_malloc_atomic() failed\n");
return -1;
}
len += snprintf(info, infolen, "%s\n\n", driver_template.name);
len += snprintf(&info[len], infolen - len, "Write file to reset counters (e.g., \"echo reset > /proc/scsi/iscsi/2\").\n\n");
len += snprintf(&info[len], infolen - len, "--------------------\n");
len += snprintf(&info[len], infolen - len, "Driver Configuration\n");
len += snprintf(&info[len], infolen - len, "--------------------\n\n");
len += snprintf(&info[len], infolen - len, " CONFIG_INITIATOR_NUM_TARGETS: %u\n", CONFIG_INITIATOR_NUM_TARGETS);
len += snprintf(&info[len], infolen - len, " CONFIG_INITIATOR_QUEUE_DEPTH: %u\n\n", CONFIG_INITIATOR_QUEUE_DEPTH);
len += snprintf(&info[len], infolen - len, "---------------\n");
len += snprintf(&info[len], infolen - len, "SCSI Statistics\n");
len += snprintf(&info[len], infolen - len, "---------------\n\n");
len += snprintf(&info[len], infolen - len, " Tx:\n");
len += snprintf(&info[len], infolen - len, " queued: %u\n", g_stats.num_tx_queued);
len += snprintf(&info[len], infolen - len, " completed: %u\n", g_stats.num_tx);
len += snprintf(&info[len], infolen - len, " avg size: %u\n", g_stats.num_tx?(g_stats.tx/g_stats.num_tx):0);
len += snprintf(&info[len], infolen - len, " total bytes: %u MB\n", g_stats.tx/1048576 + g_stats.tx_overflow*4096);
len += snprintf(&info[len], infolen - len, " total overflow: %u\n", g_stats.tx_overflow);
len += snprintf(&info[len], infolen - len, " Rx:\n");
len += snprintf(&info[len], infolen - len, " queued: %u\n", g_stats.num_rx_queued);
len += snprintf(&info[len], infolen - len, " completed: %u\n", g_stats.num_rx);
len += snprintf(&info[len], infolen - len, " avg size: %u\n", g_stats.num_rx?(g_stats.rx/g_stats.num_rx):0);
len += snprintf(&info[len], infolen - len, " total bytes: %u MB\n", g_stats.rx/1048576 + g_stats.rx_overflow*4096);
len += snprintf(&info[len], infolen - len, " total overflow: %u\n", g_stats.rx_overflow);
len += snprintf(&info[len], infolen - len, " Errors:\n");
len += snprintf(&info[len], infolen - len, " aborts: %u\n", g_stats.aborts_success);
/* len += snprintf(&info[len], infolen - len, " failed aborts: %u\n", g_stats.aborts_failed); */
len += snprintf(&info[len], infolen - len, " device resets: %u\n", g_stats.device_resets);
len += snprintf(&info[len], infolen - len, " bus resets: %u\n", g_stats.bus_resets);
len += snprintf(&info[len], infolen - len, " host resets: %u\n", g_stats.host_resets);
len += snprintf(&info[len], infolen - len, " Tx error bytes: %u\n", g_stats.tx_error);
len += snprintf(&info[len], infolen - len, " Rx error bytes: %u\n\n", g_stats.rx_error);
len += snprintf(&info[len], infolen - len, "--------------------\n");
len += snprintf(&info[len], infolen - len, "iSCSI Initiator Info\n");
len += snprintf(&info[len], infolen - len, "--------------------\n\n");
if ((len += initiator_info(&info[len], infolen, len))==-1) {
iscsi_trace_error("initiator_info() failed\n");
if (info != NULL) iscsi_free_atomic(info);
return -1;
}
if (offset>len-1) {
len = 0;
} else if (offset+length>len-1) {
len -= offset;
} else {
len = length;
}
*start = buffer;
memcpy(buffer, info+offset, len);
if (info != NULL) iscsi_free_atomic(info);
return len;
}
}
int iscsi_ioctl (Scsi_Device *dev, int cmd, void *argp) {
int i;
int lun = 0;
/* Run tests for each target */
for (i=0; i<CONFIG_INITIATOR_NUM_TARGETS; i++) {
if (test_all(i, lun)!=0) {
iscsi_trace_error("test_all() failed\n");
return -1;
}
}
return 0;
}
void iscsi_select_queue_depths(struct Scsi_Host *host, Scsi_Device *scsi_devs) {
struct scsi_device *device;
for (device = scsi_devs; device; device = device->next) {
if (device->host != host) {
iscsi_trace_error("got device for different host\n");
continue;
}
if (device->tagged_supported) {
iscsi_trace(TRACE_SCSI_DEBUG, "target %i lun %i supports TCQ\n", device->id, device->lun);
device->tagged_queue = 1;
device->current_tag = 0;
device->queue_depth = CONFIG_INITIATOR_QUEUE_DEPTH;
iscsi_trace(TRACE_SCSI_DEBUG, "device queue depth set to %i\n", device->queue_depth);
} else {
iscsi_trace(TRACE_SCSI_DEBUG, "target %i lun %i does NOT support TCQ\n", device->id, device->lun);
device->queue_depth = 1;
}
}
}
/*
* Error Handling Routines
*/
int iscsi_abort_handler (Scsi_Cmnd *SCpnt) {
iscsi_scsi_cmd_args_t *scsi_cmd;
initiator_session_t *sess;
int i;
unsigned long flags;
spin_unlock_irq(&io_request_lock);
iscsi_trace_error("aborting SCSI cmd 0x%p (op 0x%x, tid %i, lun %i)\n",
SCpnt, SCpnt->cmnd[0], SCpnt->target, SCpnt->lun);
for (i=0; i<CONFIG_INITIATOR_QUEUE_DEPTH; i++) {
/* Find the cmd ptr in g_cmd. We look for the callback_arg that's equal */
/* to SCpnt. iscsi_done() sets callback_arg to NULL when a command */
/* completes. So we know that any non_NULL callback_arg is associated */
/* with an outstanding command. */
if (g_cmd[i].callback_arg == SCpnt) {
/* Abort the command */
if (initiator_abort(&g_cmd[i])!=0) {
iscsi_trace_error("initiator_abort() failed\n");
spin_lock_irq(&io_request_lock);
return FAILED;
}
/* Update counters */
scsi_cmd = (iscsi_scsi_cmd_args_t *) g_cmd[i].ptr;
if ((scsi_cmd->input)&&(scsi_cmd->output)) {
iscsi_trace_error("bidi xfers not implemented\n");
spin_lock_irq(&io_request_lock);
return FAILED;
} else if (scsi_cmd->input) {
iscsi_spin_lock_irqsave(&g_stats.lock,&flags);
g_stats.rx_error += SCpnt->request_bufflen;
g_stats.rx_queued -= SCpnt->request_bufflen;
iscsi_spin_unlock_irqrestore(&g_stats.lock, &flags);
} else if (scsi_cmd->output) {
iscsi_spin_lock_irqsave(&g_stats.lock, &flags);
g_stats.tx_error += SCpnt->request_bufflen;
g_stats.tx_queued -= SCpnt->request_bufflen;
iscsi_spin_unlock_irqrestore(&g_stats.lock, &flags);
}
break;
}
}
/* Destroy session */
if (g_target[SCpnt->target].has_session) {
sess = g_target[SCpnt->target].sess;
#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
if (in_interrupt()) {
iscsi_trace_error("aborting within interrupt (killing Tx and Rx threads)\n");
#endif
iscsi_trace_error("killing Tx and Rx threads\n");
kill_proc(sess->rx_worker.pid, SIGKILL, 1);
kill_proc(sess->tx_worker.pid, SIGKILL, 1);
sess->tx_worker.state = 0;
sess->rx_worker.state = 0;
#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,0)
} else {
iscsi_trace_error("aborting outside interrupt (gracefully ending Tx and Rx)\n");
}
#endif
iscsi_trace(TRACE_ISCSI_DEBUG, "destroying session\n");
if (session_destroy_i(sess)!=0) {
iscsi_trace_error("session_destroy() failed\n");
g_stats.aborts_failed++;
spin_lock_irq(&io_request_lock);
return FAILED;
}
} else {
iscsi_trace(TRACE_ISCSI_DEBUG, "no session\n");
}
g_stats.aborts_success++;
iscsi_trace_error("successfully aborted SCSI cmd 0x%p (op 0x%x, tid %i, lun %i)\n",
SCpnt, SCpnt->cmnd[0], SCpnt->target, SCpnt->lun);
spin_lock_irq(&io_request_lock);
return SUCCESS;
}
int iscsi_device_reset_handler (Scsi_Cmnd *SCpnt) {
iscsi_trace_error("***********************\n");
iscsi_trace_error("*** DEVICE %i RESET ***\n", SCpnt->target);
iscsi_trace_error("***********************\n");
g_stats.device_resets++;
return SUCCESS;
}
int iscsi_bus_reset_handler (Scsi_Cmnd *SCpnt) {
iscsi_trace_error("********************\n");
iscsi_trace_error("*** BUS %i RESET ***\n", SCpnt->target);
iscsi_trace_error("********************\n");
g_stats.bus_resets++;
return SUCCESS;
}
int iscsi_host_reset_handler(Scsi_Cmnd *SCpnt) {
iscsi_trace_error("*********************\n");
iscsi_trace_error("*** HOST RESET %i ***\n", SCpnt->target);
iscsi_trace_error("*********************\n");
g_stats.host_resets++;
return SUCCESS;
}
syntax highlighted by Code2HTML, v. 0.9.1