/*
* Copyright (C) 2002-2003 Ardis Technolgies <roman@ardistech.com>
*
* Released under the terms of the GNU GPL v2.0.
*/
#include <linux/module.h>
#include <linux/hash.h>
#include <net/tcp.h>
#include <scsi/scsi.h>
#include "iscsi.h"
#include "iscsi_dbg.h"
#include "iotype.h"
unsigned long debug_enable_flags;
static struct kmem_cache *iscsi_cmnd_cache;
static u8 dummy_data[PAGE_SIZE];
static int ctr_major;
static char ctr_name[] = "ietctl";
extern struct file_operations ctr_fops;
static u32 cmnd_write_size(struct iscsi_cmnd *cmnd)
{
struct iscsi_scsi_cmd_hdr *hdr = cmnd_hdr(cmnd);
if (hdr->flags & ISCSI_CMD_WRITE)
return be32_to_cpu(hdr->data_length);
return 0;
}
static u32 cmnd_read_size(struct iscsi_cmnd *cmnd)
{
struct iscsi_scsi_cmd_hdr *hdr = cmnd_hdr(cmnd);
if (hdr->flags & ISCSI_CMD_READ) {
struct iscsi_rlength_ahdr *ahdr =
(struct iscsi_rlength_ahdr *)cmnd->pdu.ahs;
if (!(hdr->flags & ISCSI_CMD_WRITE))
return be32_to_cpu(hdr->data_length);
if (ahdr && ahdr->ahstype == ISCSI_AHSTYPE_RLENGTH)
return be32_to_cpu(ahdr->read_length);
}
return 0;
}
static void iscsi_device_queue_cmnd(struct iscsi_cmnd *cmnd)
{
set_cmnd_waitio(cmnd);
wthread_queue(cmnd);
}
static void iscsi_scsi_queuecmnd(struct iscsi_cmnd *cmnd)
{
struct iscsi_queue *queue = &cmnd->lun->queue;
dprintk(D_GENERIC, "%p\n", cmnd);
if ((cmnd->pdu.bhs.flags & ISCSI_CMD_ATTR_MASK) != ISCSI_CMD_UNTAGGED &&
(cmnd->pdu.bhs.flags & ISCSI_CMD_ATTR_MASK) != ISCSI_CMD_SIMPLE) {
cmnd->pdu.bhs.flags &= ~ISCSI_CMD_ATTR_MASK;
cmnd->pdu.bhs.flags |= ISCSI_CMD_UNTAGGED;
}
spin_lock(&queue->queue_lock);
set_cmnd_queued(cmnd);
switch (cmnd->pdu.bhs.flags & ISCSI_CMD_ATTR_MASK) {
case ISCSI_CMD_UNTAGGED:
case ISCSI_CMD_SIMPLE:
if (!list_empty(&queue->wait_list) || queue->ordered_cmnd)
goto pending;
queue->active_cnt++;
break;
default:
BUG();
}
spin_unlock(&queue->queue_lock);
iscsi_device_queue_cmnd(cmnd);
return;
pending:
assert(list_empty(&cmnd->list));
list_add_tail(&cmnd->list, &queue->wait_list);
spin_unlock(&queue->queue_lock);
return;
}
static void iscsi_scsi_dequeuecmnd(struct iscsi_cmnd *cmnd)
{
struct iscsi_queue *queue;
if (!cmnd->lun)
return;
queue = &cmnd->lun->queue;
spin_lock(&queue->queue_lock);
switch (cmnd->pdu.bhs.flags & ISCSI_CMD_ATTR_MASK) {
case ISCSI_CMD_UNTAGGED:
case ISCSI_CMD_SIMPLE:
--queue->active_cnt;
break;
case ISCSI_CMD_ORDERED:
case ISCSI_CMD_HEAD_OF_QUEUE:
case ISCSI_CMD_ACA:
BUG();
default:
/* Should the iscsi_scsi_queuecmnd func reject this ? */
break;
}
while (!list_empty(&queue->wait_list)) {
cmnd = list_entry(queue->wait_list.next, struct iscsi_cmnd, list);
switch ((cmnd->pdu.bhs.flags & ISCSI_CMD_ATTR_MASK)) {
case ISCSI_CMD_UNTAGGED:
case ISCSI_CMD_SIMPLE:
list_del_init(&cmnd->list);
queue->active_cnt++;
iscsi_device_queue_cmnd(cmnd);
break;
case ISCSI_CMD_ORDERED:
case ISCSI_CMD_HEAD_OF_QUEUE:
case ISCSI_CMD_ACA:
BUG();
}
}
spin_unlock(&queue->queue_lock);
return;
}
/**
* create a new command.
*
* iscsi_cmnd_create -
* @conn: ptr to connection (for i/o)
*
* @return ptr to command or NULL
*/
struct iscsi_cmnd *cmnd_alloc(struct iscsi_conn *conn, int req)
{
struct iscsi_cmnd *cmnd;
/* TODO: async interface is necessary ? */
cmnd = kmem_cache_alloc(iscsi_cmnd_cache, GFP_KERNEL|__GFP_NOFAIL);
memset(cmnd, 0, sizeof(*cmnd));
INIT_LIST_HEAD(&cmnd->list);
INIT_LIST_HEAD(&cmnd->pdu_list);
INIT_LIST_HEAD(&cmnd->conn_list);
INIT_LIST_HEAD(&cmnd->hash_list);
cmnd->conn = conn;
spin_lock(&conn->list_lock);
atomic_inc(&conn->nr_cmnds);
if (req)
list_add_tail(&cmnd->conn_list, &conn->pdu_list);
spin_unlock(&conn->list_lock);
cmnd->tio = NULL;
dprintk(D_GENERIC, "%p:%p\n", conn, cmnd);
return cmnd;
}
/**
* create a new command used as response.
*
* iscsi_cmnd_create_rsp_cmnd -
* @cmnd: ptr to request command
*
* @return ptr to response command or NULL
*/
static struct iscsi_cmnd *iscsi_cmnd_create_rsp_cmnd(struct iscsi_cmnd *cmnd, int final)
{
struct iscsi_cmnd *rsp = cmnd_alloc(cmnd->conn, 0);
if (final)
set_cmnd_final(rsp);
list_add_tail(&rsp->pdu_list, &cmnd->pdu_list);
rsp->req = cmnd;
return rsp;
}
static struct iscsi_cmnd *get_rsp_cmnd(struct iscsi_cmnd *req)
{
return list_entry(req->pdu_list.prev, struct iscsi_cmnd, pdu_list);
}
static void iscsi_cmnds_init_write(struct list_head *send)
{
struct iscsi_cmnd *cmnd = list_entry(send->next, struct iscsi_cmnd, list);
struct iscsi_conn *conn = cmnd->conn;
struct list_head *pos, *next;
spin_lock(&conn->list_lock);
list_for_each_safe(pos, next, send) {
cmnd = list_entry(pos, struct iscsi_cmnd, list);
dprintk(D_GENERIC, "%p:%x\n", cmnd, cmnd_opcode(cmnd));
list_del_init(&cmnd->list);
assert(conn == cmnd->conn);
list_add_tail(&cmnd->list, &conn->write_list);
}
spin_unlock(&conn->list_lock);
nthread_wakeup(conn->session->target);
}
static void iscsi_cmnd_init_write(struct iscsi_cmnd *cmnd)
{
LIST_HEAD(head);
if (!list_empty(&cmnd->list)) {
eprintk("%x %x %x %x %lx %u %u %u %u %u %u %u %d %d\n",
cmnd_itt(cmnd), cmnd_ttt(cmnd), cmnd_opcode(cmnd),
cmnd_scsicode(cmnd), cmnd->flags, cmnd->r2t_sn,
cmnd->r2t_length, cmnd->is_unsolicited_data,
cmnd->target_task_tag, cmnd->outstanding_r2t,
cmnd->hdigest, cmnd->ddigest,
list_empty(&cmnd->pdu_list), list_empty(&cmnd->hash_list));
assert(list_empty(&cmnd->list));
}
list_add(&cmnd->list, &head);
iscsi_cmnds_init_write(&head);
}
static void do_send_data_rsp(struct iscsi_cmnd *cmnd)
{
struct iscsi_conn *conn = cmnd->conn;
struct iscsi_cmnd *data_cmnd;
struct tio *tio = cmnd->tio;
struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd);
struct iscsi_data_in_hdr *rsp;
u32 pdusize, expsize, scsisize, size, offset, sn;
LIST_HEAD(send);
dprintk(D_GENERIC, "%p\n", cmnd);
pdusize = conn->session->param.max_xmit_data_length;
expsize = cmnd_read_size(cmnd);
size = min(expsize, tio->size);
offset = 0;
sn = 0;
while (1) {
data_cmnd = iscsi_cmnd_create_rsp_cmnd(cmnd, size <= pdusize);
tio_get(tio);
data_cmnd->tio = tio;
rsp = (struct iscsi_data_in_hdr *)&data_cmnd->pdu.bhs;
rsp->opcode = ISCSI_OP_SCSI_DATA_IN;
rsp->itt = req->itt;
rsp->ttt = cpu_to_be32(ISCSI_RESERVED_TAG);
rsp->buffer_offset = offset;
rsp->data_sn = cpu_to_be32(sn);
if (size <= pdusize) {
data_cmnd->pdu.datasize = size;
rsp->flags = ISCSI_FLG_FINAL | ISCSI_FLG_STATUS;
scsisize = tio->size;
if (scsisize < expsize) {
rsp->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW;
size = expsize - scsisize;
} else if (scsisize > expsize) {
rsp->flags |= ISCSI_FLG_RESIDUAL_OVERFLOW;
size = scsisize - expsize;
} else
size = 0;
rsp->residual_count = cpu_to_be32(size);
list_add_tail(&data_cmnd->list, &send);
break;
}
data_cmnd->pdu.datasize = pdusize;
size -= pdusize;
offset += pdusize;
sn++;
list_add_tail(&data_cmnd->list, &send);
}
iscsi_cmnds_init_write(&send);
}
static struct iscsi_cmnd *create_scsi_rsp(struct iscsi_cmnd *req)
{
struct iscsi_cmnd *rsp;
struct iscsi_scsi_cmd_hdr *req_hdr = cmnd_hdr(req);
struct iscsi_scsi_rsp_hdr *rsp_hdr;
rsp = iscsi_cmnd_create_rsp_cmnd(req, 1);
rsp_hdr = (struct iscsi_scsi_rsp_hdr *)&rsp->pdu.bhs;
rsp_hdr->opcode = ISCSI_OP_SCSI_RSP;
rsp_hdr->flags = ISCSI_FLG_FINAL;
rsp_hdr->response = ISCSI_RESPONSE_COMMAND_COMPLETED;
rsp_hdr->cmd_status = SAM_STAT_GOOD;
rsp_hdr->itt = req_hdr->itt;
return rsp;
}
static struct iscsi_cmnd *create_sense_rsp(struct iscsi_cmnd *req,
u8 sense_key, u8 asc, u8 ascq)
{
struct iscsi_cmnd *rsp;
struct iscsi_scsi_rsp_hdr *rsp_hdr;
struct tio *tio;
struct iscsi_sense_data *sense;
rsp = iscsi_cmnd_create_rsp_cmnd(req, 1);
rsp_hdr = (struct iscsi_scsi_rsp_hdr *)&rsp->pdu.bhs;
rsp_hdr->opcode = ISCSI_OP_SCSI_RSP;
rsp_hdr->flags = ISCSI_FLG_FINAL;
rsp_hdr->response = ISCSI_RESPONSE_COMMAND_COMPLETED;
rsp_hdr->cmd
iscsitarget-0.4.17
3星 · 超过75%的资源 需积分: 9 12 浏览量
2008-12-10
17:27:14
上传
评论
收藏 105KB GZ 举报
Leejun527
- 粉丝: 14
- 资源: 3
最新资源
- 校园帮项目,毕业设计/课程设计/javaWeb/SSM
- C++ plotting library,matplotlib-cpp-master.zip
- 案例源码matplotlib-examples-master.zip
- 基于JavaScript 实现的KMP 算法
- 基于C++实现二叉树的创建,遍历,添加,查找与删除
- 基于C语言实现二叉树的基本操作
- 毕业设计基于STM32的测量温度与压力的数据处理设计C语言完整源码+论文.zip
- 基于MATLAB的PCA算法人脸识别项目源码+GUI界面+说明文档.zip
- 基于STM32的测量温度与压力的数据处理设计源码+论文(毕业设计).zip
- Vision Transformer 网络对不同氨气氧气浓度轨迹RAS 图像数据集的分类,包含训练权重和数据集、迁移学习
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈