📄 iscsi.c
字号:
/* * 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 kmem_cache_t *iscsi_cmnd_cache;static char dummy_data[1024];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) { if (!(hdr->flags & ISCSI_CMD_WRITE)) return be32_to_cpu(hdr->data_length); if (hdr->flags & ISCSI_CMD_READ) { struct iscsi_rlength_ahdr *ahdr = (struct iscsi_rlength_ahdr *)cmnd->pdu.ahs; 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 %lx %u %u %u %u %u %u %u %d %d\n", cmnd_itt(cmnd), cmnd_ttt(cmnd), cmnd_opcode(cmnd), cmnd_scsicode(cmnd), cmnd->state, 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;}void send_scsi_rsp(struct iscsi_cmnd *req, int (*func)(struct iscsi_cmnd *)){ struct iscsi_cmnd *rsp; struct iscsi_scsi_rsp_hdr *rsp_hdr; u32 size; rsp = create_scsi_rsp(req); rsp_hdr = (struct iscsi_scsi_rsp_hdr *) &rsp->pdu.bhs; if ((size = cmnd_read_size(req)) != 0) { rsp_hdr->flags |= ISCSI_FLG_RESIDUAL_UNDERFLOW; rsp_hdr->residual_count = cpu_to_be32(size); } if (func(req) < 0) eprintk("%x\n", cmnd_opcode(req)); iscsi_cmnd_init_write(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_status = SAM_STAT_CHECK_CONDITION; rsp_hdr->itt = cmnd_hdr(req)->itt; tio = rsp->tio = tio_alloc(1); sense = (struct iscsi_sense_data *) page_address(tio->pvec[0]); assert(sense); clear_page(sense); sense->length = cpu_to_be16(14); sense->data[0] = 0xf0; sense->data[2] = sense_key; sense->data[7] = 6; // Additional sense length sense->data[12] = asc; sense->data[13] = ascq; rsp->pdu.datasize = sizeof(struct iscsi_sense_data) + 14; tio->size = (rsp->pdu.datasize + 3) & -4; tio->offset = 0; return rsp;}void send_data_rsp(struct iscsi_cmnd *req, int (*func)(struct iscsi_cmnd *)){ struct iscsi_cmnd *rsp; if (func(req) < 0) { rsp = create_sense_rsp(req, ILLEGAL_REQUEST, 0x24, 0x0); iscsi_cmnd_init_write(rsp); } else do_send_data_rsp(req);}/** * Free a command. * Also frees the additional header. * * iscsi_cmnd_remove - * @cmnd: ptr to command */void iscsi_cmnd_remove(struct iscsi_cmnd *cmnd){ struct iscsi_conn *conn; if (!cmnd) return; dprintk(D_GENERIC, "%p\n", cmnd); conn = cmnd->conn; kfree(cmnd->pdu.ahs); if (!list_empty(&cmnd->list)) { struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd); eprintk("cmnd %p still on some list?, %x, %x, %x, %x, %x, %x, %x %lx %lx\n", cmnd, req->opcode, req->scb[0], req->flags, req->itt, be32_to_cpu(req->data_length), req->cmd_sn, be32_to_cpu(cmnd->pdu.datasize), cmnd->state, conn->state); if (cmnd->req) { struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd->req); eprintk("%p %x %u\n", req, req->opcode, req->scb[0]); } dump_stack(); BUG(); } list_del(&cmnd->list); spin_lock(&conn->list_lock); atomic_dec(&conn->nr_cmnds); list_del(&cmnd->conn_list); spin_unlock(&conn->list_lock); if (cmnd->tio) tio_put(cmnd->tio); kmem_cache_free(iscsi_cmnd_cache, cmnd);}static void cmnd_skip_pdu(struct iscsi_cmnd *cmnd){ struct iscsi_conn *conn = cmnd->conn; struct tio *tio = cmnd->tio; char *addr; u32 size; int i; eprintk("%x %x %x %u\n", cmnd_itt(cmnd), cmnd_opcode(cmnd), cmnd_hdr(cmnd)->scb[0], cmnd->pdu.datasize); if (!(size = cmnd->pdu.datasize)) return; if (tio) assert(tio->pg_cnt > 0); else tio = cmnd->tio = tio_alloc(1); addr = page_address(tio->pvec[0]); assert(addr); size = (size + 3) & -4; conn->read_size = size; for (i = 0; size > PAGE_CACHE_SIZE; i++, size -= PAGE_CACHE_SIZE) { assert(i < ISCSI_CONN_IOV_MAX); conn->read_iov[i].iov_base = addr; conn->read_iov[i].iov_len = PAGE_CACHE_SIZE; } conn->read_iov[i].iov_base = addr; conn->read_iov[i].iov_len = size; conn->read_msg.msg_iov = conn->read_iov; conn->read_msg.msg_iovlen = ++i;}static void iscsi_cmnd_reject(struct iscsi_cmnd *req, int reason){ struct iscsi_cmnd *rsp; struct iscsi_reject_hdr *rsp_hdr; struct tio *tio; char *addr; rsp = iscsi_cmnd_create_rsp_cmnd(req, 1); rsp_hdr = (struct iscsi_reject_hdr *)&rsp->pdu.bhs; rsp_hdr->opcode = ISCSI_OP_REJECT; rsp_hdr->ffffffff = ISCSI_RESERVED_TAG; rsp_hdr->reason = reason; rsp->tio = tio = tio_alloc(1); addr = page_address(tio->pvec[0]); clear_page(addr); memcpy(addr, &req->pdu.bhs, sizeof(struct iscsi_hdr)); tio->size = rsp->pdu.datasize = sizeof(struct iscsi_hdr); cmnd_skip_pdu(req); req->pdu.bhs.opcode = ISCSI_OP_PDU_REJECT;}static void cmnd_set_sn(struct iscsi_cmnd *cmnd, int set_stat_sn){ struct iscsi_conn *conn = cmnd->conn; struct iscsi_session *sess = conn->session; if (set_stat_sn) cmnd->pdu.bhs.sn = cpu_to_be32(conn->stat_sn++); cmnd->pdu.bhs.exp_sn = cpu_to_be32(sess->exp_cmd_sn); cmnd->pdu.bhs.max_sn = cpu_to_be32(sess->exp_cmd_sn + sess->max_queued_cmnds);}static void update_stat_sn(struct iscsi_cmnd *cmnd){ struct iscsi_conn *conn = cmnd->conn; u32 exp_stat_sn; cmnd->pdu.bhs.exp_sn = exp_stat_sn = be32_to_cpu(cmnd->pdu.bhs.exp_sn); dprintk(D_GENERIC, "%x,%x\n", cmnd_opcode(cmnd), exp_stat_sn); if ((int)(exp_stat_sn - conn->exp_stat_sn) > 0 && (int)(exp_stat_sn - conn->stat_sn) <= 0) { // free pdu resources cmnd->conn->exp_stat_sn = exp_stat_sn; }}static int check_cmd_sn(struct iscsi_cmnd *cmnd){ struct iscsi_session *session = cmnd->conn->session; u32 cmd_sn; cmnd->pdu.bhs.sn = cmd_sn = be32_to_cpu(cmnd->pdu.bhs.sn); dprintk(D_GENERIC, "%d(%d)\n", cmd_sn, session->exp_cmd_sn); if ((s32)(cmd_sn - session->exp_cmd_sn) >= 0) return 0; eprintk("sequence error (%x,%x)\n", cmd_sn, session->exp_cmd_sn); return -ISCSI_REASON_PROTOCOL_ERROR;}static struct iscsi_cmnd *__cmnd_find_hash(struct iscsi_session *session, u32 itt, u32 ttt){ struct list_head *head; struct iscsi_cmnd *cmnd; head = &session->cmnd_hash[cmnd_hashfn(itt)]; list_for_each_entry(cmnd, head, hash_list) { if (cmnd->pdu.bhs.itt == itt) { if ((ttt != ISCSI_RESERVED_TAG) && (ttt != cmnd->target_task_tag)) continue; return cmnd; } } return NULL;}static struct iscsi_cmnd *cmnd_find_hash(struct iscsi_session *session, u32 itt, u32 ttt){ struct iscsi_cmnd *cmnd; spin_lock(&session->cmnd_hash_lock); cmnd = __cmnd_find_hash(session, itt, ttt); spin_unlock(&session->cmnd_hash_lock); return cmnd;}static int cmnd_insert_hash(struct iscsi_cmnd *cmnd){ struct iscsi_session *session = cmnd->conn->session; struct iscsi_cmnd *tmp; struct list_head *head; int err = 0; u32 itt = cmnd->pdu.bhs.itt; dprintk(D_GENERIC, "%p:%x\n", cmnd, itt); if (itt == ISCSI_RESERVED_TAG) { err = -ISCSI_REASON_PROTOCOL_ERROR;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -