📄 iscsi-xmit-pdu.c
字号:
/* * iSCSI driver for Linux * Copyright (C) 2001 Cisco Systems, Inc. * Copyright (C) 2004 Mike Christie * Copyright (C) 2004 IBM Corporation * maintained by linux-iscsi-devel@lists.sourceforge.net * * 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; either version 2 of the License, or * (at your option) any later version. * * 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. * * See the file COPYING included with this distribution for more details. * * $Id: iscsi-xmit-pdu.c,v 1.49 2005/01/19 23:00:40 mikenc Exp $ * * Contains functions to handle transmission of iSCSI PDUs */#include <linux/tcp.h>#include <linux/net.h>#include <asm/scatterlist.h>#include <linux/scatterlist.h>#include <scsi/scsi_device.h>#include <scsi/scsi_dbg.h>#include "iscsi-session.h"#include "iscsi-task.h"#include "iscsi-protocol.h"#include "iscsi-login.h"#include "iscsi-sfnet.h"static intiscsi_send_header(struct iscsi_session *session, struct iscsi_hdr *hdr, int hdr_digest){ struct scatterlist sg; struct kvec iov[2]; u32 crc32c; int len, iovn = 0; iov[iovn].iov_base = hdr; iov[iovn].iov_len = sizeof(*hdr); len = iov[iovn].iov_len; iovn++; if (hdr_digest == ISCSI_DIGEST_CRC32C) { crypto_digest_init(session->tx_tfm); sg_init_one(&sg, (u8 *)hdr, len); crypto_digest_digest(session->tx_tfm, &sg, 1, (u8*)&crc32c); iov[iovn].iov_base = &crc32c; iov[iovn].iov_len = sizeof(crc32c); len += iov[iovn].iov_len; iovn++; } return iscsi_sendmsg(session, iov, iovn, len);}static intsend_extra_data(struct iscsi_session *session, u32 data_len, int digest_opt){ struct scatterlist sg; struct kvec iov[2]; int pad, iovn = 0, len = 0; char padding[PAD_WORD_LEN - 1]; u32 data_crc32c; if (data_len % PAD_WORD_LEN) { pad = PAD_WORD_LEN - (data_len % PAD_WORD_LEN); memset(padding, 0, pad); iov[iovn].iov_base = padding; iov[iovn].iov_len = pad; iovn++; len += pad; if (digest_opt == ISCSI_DIGEST_CRC32C) { sg_init_one(&sg, padding, pad); crypto_digest_update(session->tx_tfm, &sg, 1); } } if (data_len && digest_opt == ISCSI_DIGEST_CRC32C) { crypto_digest_final(session->tx_tfm, (u8*)&data_crc32c); iov[iovn].iov_base = &data_crc32c; iov[iovn].iov_len = sizeof(data_crc32c); len += iov[iovn].iov_len; iovn++; } if (iov) return iscsi_sendmsg(session, iov, iovn, len); else return ISCSI_IO_SUCCESS;}/** * iscsi_send_sg_data - send SCSI data * @session: iscsi session * @sglist: scatterlist * @start_sg: index into sglist to start from * @sg_offset: offset in scatterlist entry to start from * @sglist_len: number of entries in sglist * @data_len: transfer length * @digest_opt: CRC32C or NONE * * Note: * iscsi_send_sg_data will set start_sg and sg_offset to the * next starting values for future transfers from this scatterlist * (if one is possible), for the caller. **/static intiscsi_send_sg_data(struct iscsi_session *session, struct scatterlist *sglist, int *start_sg, u32 *sg_offset, int sglist_len, u32 data_len, int digest_opt){ unsigned int len, sg_bytes, pg_offset, remaining = data_len; struct scatterlist tmpsg, *sg; struct page *pg; int i, rc, flags = MSG_MORE; if (digest_opt == ISCSI_DIGEST_CRC32C) crypto_digest_init(session->tx_tfm); /* * loop over the scatterlist */ for (i = *start_sg; remaining > 0 && i < sglist_len; i++) { sg = &sglist[i]; if (signal_pending(current)) return ISCSI_IO_INTR; pg_offset = sg->offset + *sg_offset; pg = sg->page + (pg_offset >> PAGE_SHIFT); pg_offset -= (pg_offset & PAGE_MASK); /* * set the offset and sg for the next pdu or loop * iteration */ sg_bytes = sg->length - *sg_offset; if (sg_bytes <= remaining) { (*start_sg)++; *sg_offset = 0; } else { *sg_offset = *sg_offset + remaining; sg_bytes = remaining; } remaining -= sg_bytes; /* * loop over each page in sg entry */ for (; sg_bytes > 0; sg_bytes -= len) { len = min_t(unsigned int, sg_bytes, PAGE_SIZE - pg_offset); if (len == sg_bytes) flags = 0; rc = iscsi_sendpage(session, flags, pg, pg_offset, len); if (rc != ISCSI_IO_SUCCESS) return rc; if (digest_opt == ISCSI_DIGEST_CRC32C) { tmpsg.page = pg; tmpsg.offset = pg_offset; tmpsg.length = len; crypto_digest_update(session->tx_tfm, &tmpsg, 1); } pg++; pg_offset = 0; } } /* * this should only happen for driver or scsi/block layer bugs */ if (remaining != 0) { iscsi_host_err(session, "iscsi_send_sg_data - invalid sg list " "start_sg %d, sg_offset %u, sglist_len %d " "data_len %u, remaining %u\n", *start_sg, *sg_offset, sglist_len, data_len, remaining); return ISCSI_IO_INVALID_OP; } return send_extra_data(session, data_len, digest_opt);}intiscsi_send_pdu(struct iscsi_session *session, struct iscsi_hdr *hdr, int hdr_digest, char *data, int data_digest, int timeout){ struct scatterlist sg; u32 data_len, offset = 0; int rc, index = 0; /* set a timer, for the login api */ if (timeout) session->login_phase_timer = jiffies + (timeout * HZ); rc = iscsi_send_header(session, hdr, hdr_digest); if (rc != ISCSI_IO_SUCCESS) { iscsi_drop_session(session); goto done; } data_len= ntoh24(hdr->dlength); if (data && data_len) { sg_init_one(&sg, data, data_len); rc = iscsi_send_sg_data(session, &sg, &index, &offset, 1, data_len, data_digest); if (rc != ISCSI_IO_SUCCESS) iscsi_drop_session(session); } done: session->login_phase_timer = 0; return rc == ISCSI_IO_SUCCESS ? 1 : 0;}static voidset_task_mgmt_attrs(struct iscsi_scsi_task_mgmt_hdr *ststmh, struct iscsi_task *task){ u8 tmf_code; if (test_bit(ISCSI_TASK_ABORT, &task->flags)) { /* * we reused cmdsn for refcmdsn for abort tasks. */ ststmh->refcmdsn = htonl(task->cmdsn); ststmh->rtt = htonl(task->rtt); ststmh->lun[1] = task->lun; tmf_code = ISCSI_TMF_ABORT_TASK; } else if (test_bit(ISCSI_TASK_ABORT_TASK_SET, &task->flags)) { ststmh->lun[1] = task->lun; tmf_code = ISCSI_TMF_ABORT_TASK_SET; } else if (test_bit(ISCSI_TASK_LU_RESET, &task->flags)) { ststmh->lun[1] = task->lun; tmf_code = ISCSI_TMF_LOGICAL_UNIT_RESET; } else tmf_code = ISCSI_TMF_TARGET_WARM_RESET; ststmh->flags = ISCSI_FLAG_FINAL | (tmf_code & ISCSI_FLAG_TMF_MASK);}voidiscsi_send_task_mgmt(struct iscsi_session *session){ struct iscsi_task *task; struct iscsi_scsi_task_mgmt_hdr ststmh; int rc; spin_lock_bh(&session->task_lock); task = iscsi_find_session_task(session, session->last_mgmt_itt); if (!task) { /* * timed out or session dropping */ spin_unlock_bh(&session->task_lock); return; } memset(&ststmh, 0, sizeof(ststmh)); ststmh.opcode = ISCSI_OP_TASK_MGT_REQ | ISCSI_OP_IMMEDIATE; ststmh.rtt = ISCSI_RSVD_TASK_TAG; ststmh.itt = htonl(task->itt); ststmh.cmdsn = htonl(session->cmd_sn); /* CmdSN not incremented after imm cmd */ ststmh.expstatsn = htonl(session->exp_stat_sn); set_task_mgmt_attrs(&ststmh, task); __iscsi_put_task(task); spin_unlock_bh(&session->task_lock); rc = iscsi_send_header(session, (struct iscsi_hdr *)&ststmh, session->header_digest); if (rc != ISCSI_IO_SUCCESS) { /* TODO drop session here still? */ iscsi_host_err(session, "xmit_task_mgmt failed\n"); iscsi_drop_session(session); }}/** * iscsi_send_nop_out - transmit iscsi NOP-out * @session: iscsi session * @itt: Initiator Task Tag (must be in network byte order) * @ttt: Target Transfer Tag (must be in network byte order) * @lun: when ttt is valid, lun must be set **/static void__iscsi_send_nop_out(struct iscsi_session *session, u32 itt, u32 ttt, u8 *lun){ struct iscsi_nop_out_hdr stph; int rc; memset(&stph, 0, sizeof(stph)); stph.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE; stph.flags = ISCSI_FLAG_FINAL; stph.cmdsn = htonl(session->cmd_sn); stph.expstatsn = htonl(session->exp_stat_sn); if (lun) memcpy(stph.lun, lun, sizeof(stph.lun)); stph.ttt = ttt; stph.itt = itt; rc = iscsi_send_header(session, (struct iscsi_hdr *)&stph, session->header_digest); if (rc != ISCSI_IO_SUCCESS) { iscsi_host_err(session, "xmit_ping failed\n"); /* mv drop ? */ iscsi_drop_session(session); }}voidiscsi_send_nop_out(struct iscsi_session *session){ u32 itt; spin_lock_bh(&session->task_lock); itt = iscsi_alloc_itt(session); spin_unlock_bh(&session->task_lock); __iscsi_send_nop_out(session, htonl(itt), ISCSI_RSVD_TASK_TAG, NULL);}/* send replies for NopIns that requested them */voidiscsi_send_nop_replys(struct iscsi_session *session){ struct iscsi_nop_info *nop_info; /* * these aren't really tasks, but it's not worth having * a separate lock for them */ spin_lock_bh(&session->task_lock); /* * space for one data-less reply is preallocated in * the session itself */ if (session->nop_reply.ttt != ISCSI_RSVD_TASK_TAG) { spin_unlock_bh(&session->task_lock); __iscsi_send_nop_out(session, ISCSI_RSVD_TASK_TAG, session->nop_reply.ttt, session->nop_reply.lun); session->nop_reply.ttt = ISCSI_RSVD_TASK_TAG; spin_lock_bh(&session->task_lock); } /* * if we get multiple reply requests, or they have data, * they'll get queued up */ while (!list_empty(&session->nop_reply_list)) { nop_info = list_entry(session->nop_reply_list.next, struct iscsi_nop_info, reply_list); list_del_init(&nop_info->reply_list); spin_unlock_bh(&session->task_lock); __iscsi_send_nop_out(session, ISCSI_RSVD_TASK_TAG, nop_info->ttt, nop_info->lun); kfree(nop_info); if (signal_pending(current)) return; spin_lock_bh(&session->task_lock); } spin_unlock_bh(&session->task_lock);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -