⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 iscsi-xmit-pdu.c

📁 iSCSI协议在LINUX下的源码.源代码是IBM公布的.主要是结合其OSD设备用的.
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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 + -