162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * iSCSI over TCP/IP Data-Path lib 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2004 Dmitry Yusupov 662306a36Sopenharmony_ci * Copyright (C) 2004 Alex Aizman 762306a36Sopenharmony_ci * Copyright (C) 2005 - 2006 Mike Christie 862306a36Sopenharmony_ci * Copyright (C) 2006 Red Hat, Inc. All rights reserved. 962306a36Sopenharmony_ci * maintained by open-iscsi@googlegroups.com 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Credits: 1262306a36Sopenharmony_ci * Christoph Hellwig 1362306a36Sopenharmony_ci * FUJITA Tomonori 1462306a36Sopenharmony_ci * Arne Redlich 1562306a36Sopenharmony_ci * Zhenyu Wang 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <crypto/hash.h> 1962306a36Sopenharmony_ci#include <linux/types.h> 2062306a36Sopenharmony_ci#include <linux/list.h> 2162306a36Sopenharmony_ci#include <linux/inet.h> 2262306a36Sopenharmony_ci#include <linux/slab.h> 2362306a36Sopenharmony_ci#include <linux/file.h> 2462306a36Sopenharmony_ci#include <linux/blkdev.h> 2562306a36Sopenharmony_ci#include <linux/delay.h> 2662306a36Sopenharmony_ci#include <linux/kfifo.h> 2762306a36Sopenharmony_ci#include <linux/scatterlist.h> 2862306a36Sopenharmony_ci#include <linux/module.h> 2962306a36Sopenharmony_ci#include <net/tcp.h> 3062306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h> 3162306a36Sopenharmony_ci#include <scsi/scsi_device.h> 3262306a36Sopenharmony_ci#include <scsi/scsi_host.h> 3362306a36Sopenharmony_ci#include <scsi/scsi.h> 3462306a36Sopenharmony_ci#include <scsi/scsi_transport_iscsi.h> 3562306a36Sopenharmony_ci#include <trace/events/iscsi.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include "iscsi_tcp.h" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ciMODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, " 4062306a36Sopenharmony_ci "Dmitry Yusupov <dmitry_yus@yahoo.com>, " 4162306a36Sopenharmony_ci "Alex Aizman <itn780@yahoo.com>"); 4262306a36Sopenharmony_ciMODULE_DESCRIPTION("iSCSI/TCP data-path"); 4362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int iscsi_dbg_libtcp; 4662306a36Sopenharmony_cimodule_param_named(debug_libiscsi_tcp, iscsi_dbg_libtcp, int, 4762306a36Sopenharmony_ci S_IRUGO | S_IWUSR); 4862306a36Sopenharmony_ciMODULE_PARM_DESC(debug_libiscsi_tcp, "Turn on debugging for libiscsi_tcp " 4962306a36Sopenharmony_ci "module. Set to 1 to turn on, and zero to turn off. Default " 5062306a36Sopenharmony_ci "is off."); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define ISCSI_DBG_TCP(_conn, dbg_fmt, arg...) \ 5362306a36Sopenharmony_ci do { \ 5462306a36Sopenharmony_ci if (iscsi_dbg_libtcp) \ 5562306a36Sopenharmony_ci iscsi_conn_printk(KERN_INFO, _conn, \ 5662306a36Sopenharmony_ci "%s " dbg_fmt, \ 5762306a36Sopenharmony_ci __func__, ##arg); \ 5862306a36Sopenharmony_ci iscsi_dbg_trace(trace_iscsi_dbg_tcp, \ 5962306a36Sopenharmony_ci &(_conn)->cls_conn->dev, \ 6062306a36Sopenharmony_ci "%s " dbg_fmt, __func__, ##arg);\ 6162306a36Sopenharmony_ci } while (0); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn, 6462306a36Sopenharmony_ci struct iscsi_segment *segment); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* 6762306a36Sopenharmony_ci * Scatterlist handling: inside the iscsi_segment, we 6862306a36Sopenharmony_ci * remember an index into the scatterlist, and set data/size 6962306a36Sopenharmony_ci * to the current scatterlist entry. For highmem pages, we 7062306a36Sopenharmony_ci * kmap as needed. 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci * Note that the page is unmapped when we return from 7362306a36Sopenharmony_ci * TCP's data_ready handler, so we may end up mapping and 7462306a36Sopenharmony_ci * unmapping the same page repeatedly. The whole reason 7562306a36Sopenharmony_ci * for this is that we shouldn't keep the page mapped 7662306a36Sopenharmony_ci * outside the softirq. 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/** 8062306a36Sopenharmony_ci * iscsi_tcp_segment_init_sg - init indicated scatterlist entry 8162306a36Sopenharmony_ci * @segment: the buffer object 8262306a36Sopenharmony_ci * @sg: scatterlist 8362306a36Sopenharmony_ci * @offset: byte offset into that sg entry 8462306a36Sopenharmony_ci * 8562306a36Sopenharmony_ci * This function sets up the segment so that subsequent 8662306a36Sopenharmony_ci * data is copied to the indicated sg entry, at the given 8762306a36Sopenharmony_ci * offset. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_cistatic inline void 9062306a36Sopenharmony_ciiscsi_tcp_segment_init_sg(struct iscsi_segment *segment, 9162306a36Sopenharmony_ci struct scatterlist *sg, unsigned int offset) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci segment->sg = sg; 9462306a36Sopenharmony_ci segment->sg_offset = offset; 9562306a36Sopenharmony_ci segment->size = min(sg->length - offset, 9662306a36Sopenharmony_ci segment->total_size - segment->total_copied); 9762306a36Sopenharmony_ci segment->data = NULL; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/** 10162306a36Sopenharmony_ci * iscsi_tcp_segment_map - map the current S/G page 10262306a36Sopenharmony_ci * @segment: iscsi_segment 10362306a36Sopenharmony_ci * @recv: 1 if called from recv path 10462306a36Sopenharmony_ci * 10562306a36Sopenharmony_ci * We only need to possibly kmap data if scatter lists are being used, 10662306a36Sopenharmony_ci * because the iscsi passthrough and internal IO paths will never use high 10762306a36Sopenharmony_ci * mem pages. 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_cistatic void iscsi_tcp_segment_map(struct iscsi_segment *segment, int recv) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct scatterlist *sg; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (segment->data != NULL || !segment->sg) 11462306a36Sopenharmony_ci return; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci sg = segment->sg; 11762306a36Sopenharmony_ci BUG_ON(segment->sg_mapped); 11862306a36Sopenharmony_ci BUG_ON(sg->length == 0); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* 12162306a36Sopenharmony_ci * We always map for the recv path. 12262306a36Sopenharmony_ci * 12362306a36Sopenharmony_ci * If the page count is greater than one it is ok to send 12462306a36Sopenharmony_ci * to the network layer's zero copy send path. If not we 12562306a36Sopenharmony_ci * have to go the slow sendmsg path. 12662306a36Sopenharmony_ci * 12762306a36Sopenharmony_ci * Same goes for slab pages: skb_can_coalesce() allows 12862306a36Sopenharmony_ci * coalescing neighboring slab objects into a single frag which 12962306a36Sopenharmony_ci * triggers one of hardened usercopy checks. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ci if (!recv && sendpage_ok(sg_page(sg))) 13262306a36Sopenharmony_ci return; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (recv) { 13562306a36Sopenharmony_ci segment->atomic_mapped = true; 13662306a36Sopenharmony_ci segment->sg_mapped = kmap_atomic(sg_page(sg)); 13762306a36Sopenharmony_ci } else { 13862306a36Sopenharmony_ci segment->atomic_mapped = false; 13962306a36Sopenharmony_ci /* the xmit path can sleep with the page mapped so use kmap */ 14062306a36Sopenharmony_ci segment->sg_mapped = kmap(sg_page(sg)); 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci segment->data = segment->sg_mapped + sg->offset + segment->sg_offset; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_civoid iscsi_tcp_segment_unmap(struct iscsi_segment *segment) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci if (segment->sg_mapped) { 14962306a36Sopenharmony_ci if (segment->atomic_mapped) 15062306a36Sopenharmony_ci kunmap_atomic(segment->sg_mapped); 15162306a36Sopenharmony_ci else 15262306a36Sopenharmony_ci kunmap(sg_page(segment->sg)); 15362306a36Sopenharmony_ci segment->sg_mapped = NULL; 15462306a36Sopenharmony_ci segment->data = NULL; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_tcp_segment_unmap); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* 16062306a36Sopenharmony_ci * Splice the digest buffer into the buffer 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_cistatic inline void 16362306a36Sopenharmony_ciiscsi_tcp_segment_splice_digest(struct iscsi_segment *segment, void *digest) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci segment->data = digest; 16662306a36Sopenharmony_ci segment->digest_len = ISCSI_DIGEST_SIZE; 16762306a36Sopenharmony_ci segment->total_size += ISCSI_DIGEST_SIZE; 16862306a36Sopenharmony_ci segment->size = ISCSI_DIGEST_SIZE; 16962306a36Sopenharmony_ci segment->copied = 0; 17062306a36Sopenharmony_ci segment->sg = NULL; 17162306a36Sopenharmony_ci segment->hash = NULL; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/** 17562306a36Sopenharmony_ci * iscsi_tcp_segment_done - check whether the segment is complete 17662306a36Sopenharmony_ci * @tcp_conn: iscsi tcp connection 17762306a36Sopenharmony_ci * @segment: iscsi segment to check 17862306a36Sopenharmony_ci * @recv: set to one of this is called from the recv path 17962306a36Sopenharmony_ci * @copied: number of bytes copied 18062306a36Sopenharmony_ci * 18162306a36Sopenharmony_ci * Check if we're done receiving this segment. If the receive 18262306a36Sopenharmony_ci * buffer is full but we expect more data, move on to the 18362306a36Sopenharmony_ci * next entry in the scatterlist. 18462306a36Sopenharmony_ci * 18562306a36Sopenharmony_ci * If the amount of data we received isn't a multiple of 4, 18662306a36Sopenharmony_ci * we will transparently receive the pad bytes, too. 18762306a36Sopenharmony_ci * 18862306a36Sopenharmony_ci * This function must be re-entrant. 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_ciint iscsi_tcp_segment_done(struct iscsi_tcp_conn *tcp_conn, 19162306a36Sopenharmony_ci struct iscsi_segment *segment, int recv, 19262306a36Sopenharmony_ci unsigned copied) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct scatterlist sg; 19562306a36Sopenharmony_ci unsigned int pad; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci ISCSI_DBG_TCP(tcp_conn->iscsi_conn, "copied %u %u size %u %s\n", 19862306a36Sopenharmony_ci segment->copied, copied, segment->size, 19962306a36Sopenharmony_ci recv ? "recv" : "xmit"); 20062306a36Sopenharmony_ci if (segment->hash && copied) { 20162306a36Sopenharmony_ci /* 20262306a36Sopenharmony_ci * If a segment is kmapd we must unmap it before sending 20362306a36Sopenharmony_ci * to the crypto layer since that will try to kmap it again. 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_ci iscsi_tcp_segment_unmap(segment); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (!segment->data) { 20862306a36Sopenharmony_ci sg_init_table(&sg, 1); 20962306a36Sopenharmony_ci sg_set_page(&sg, sg_page(segment->sg), copied, 21062306a36Sopenharmony_ci segment->copied + segment->sg_offset + 21162306a36Sopenharmony_ci segment->sg->offset); 21262306a36Sopenharmony_ci } else 21362306a36Sopenharmony_ci sg_init_one(&sg, segment->data + segment->copied, 21462306a36Sopenharmony_ci copied); 21562306a36Sopenharmony_ci ahash_request_set_crypt(segment->hash, &sg, NULL, copied); 21662306a36Sopenharmony_ci crypto_ahash_update(segment->hash); 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci segment->copied += copied; 22062306a36Sopenharmony_ci if (segment->copied < segment->size) { 22162306a36Sopenharmony_ci iscsi_tcp_segment_map(segment, recv); 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci segment->total_copied += segment->copied; 22662306a36Sopenharmony_ci segment->copied = 0; 22762306a36Sopenharmony_ci segment->size = 0; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* Unmap the current scatterlist page, if there is one. */ 23062306a36Sopenharmony_ci iscsi_tcp_segment_unmap(segment); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci /* Do we have more scatterlist entries? */ 23362306a36Sopenharmony_ci ISCSI_DBG_TCP(tcp_conn->iscsi_conn, "total copied %u total size %u\n", 23462306a36Sopenharmony_ci segment->total_copied, segment->total_size); 23562306a36Sopenharmony_ci if (segment->total_copied < segment->total_size) { 23662306a36Sopenharmony_ci /* Proceed to the next entry in the scatterlist. */ 23762306a36Sopenharmony_ci iscsi_tcp_segment_init_sg(segment, sg_next(segment->sg), 23862306a36Sopenharmony_ci 0); 23962306a36Sopenharmony_ci iscsi_tcp_segment_map(segment, recv); 24062306a36Sopenharmony_ci BUG_ON(segment->size == 0); 24162306a36Sopenharmony_ci return 0; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* Do we need to handle padding? */ 24562306a36Sopenharmony_ci if (!(tcp_conn->iscsi_conn->session->tt->caps & CAP_PADDING_OFFLOAD)) { 24662306a36Sopenharmony_ci pad = iscsi_padding(segment->total_copied); 24762306a36Sopenharmony_ci if (pad != 0) { 24862306a36Sopenharmony_ci ISCSI_DBG_TCP(tcp_conn->iscsi_conn, 24962306a36Sopenharmony_ci "consume %d pad bytes\n", pad); 25062306a36Sopenharmony_ci segment->total_size += pad; 25162306a36Sopenharmony_ci segment->size = pad; 25262306a36Sopenharmony_ci segment->data = segment->padbuf; 25362306a36Sopenharmony_ci return 0; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* 25862306a36Sopenharmony_ci * Set us up for transferring the data digest. hdr digest 25962306a36Sopenharmony_ci * is completely handled in hdr done function. 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_ci if (segment->hash) { 26262306a36Sopenharmony_ci ahash_request_set_crypt(segment->hash, NULL, 26362306a36Sopenharmony_ci segment->digest, 0); 26462306a36Sopenharmony_ci crypto_ahash_final(segment->hash); 26562306a36Sopenharmony_ci iscsi_tcp_segment_splice_digest(segment, 26662306a36Sopenharmony_ci recv ? segment->recv_digest : segment->digest); 26762306a36Sopenharmony_ci return 0; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return 1; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_tcp_segment_done); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci/** 27562306a36Sopenharmony_ci * iscsi_tcp_segment_recv - copy data to segment 27662306a36Sopenharmony_ci * @tcp_conn: the iSCSI TCP connection 27762306a36Sopenharmony_ci * @segment: the buffer to copy to 27862306a36Sopenharmony_ci * @ptr: data pointer 27962306a36Sopenharmony_ci * @len: amount of data available 28062306a36Sopenharmony_ci * 28162306a36Sopenharmony_ci * This function copies up to @len bytes to the 28262306a36Sopenharmony_ci * given buffer, and returns the number of bytes 28362306a36Sopenharmony_ci * consumed, which can actually be less than @len. 28462306a36Sopenharmony_ci * 28562306a36Sopenharmony_ci * If hash digest is enabled, the function will update the 28662306a36Sopenharmony_ci * hash while copying. 28762306a36Sopenharmony_ci * Combining these two operations doesn't buy us a lot (yet), 28862306a36Sopenharmony_ci * but in the future we could implement combined copy+crc, 28962306a36Sopenharmony_ci * just way we do for network layer checksums. 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_cistatic int 29262306a36Sopenharmony_ciiscsi_tcp_segment_recv(struct iscsi_tcp_conn *tcp_conn, 29362306a36Sopenharmony_ci struct iscsi_segment *segment, const void *ptr, 29462306a36Sopenharmony_ci unsigned int len) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci unsigned int copy = 0, copied = 0; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci while (!iscsi_tcp_segment_done(tcp_conn, segment, 1, copy)) { 29962306a36Sopenharmony_ci if (copied == len) { 30062306a36Sopenharmony_ci ISCSI_DBG_TCP(tcp_conn->iscsi_conn, 30162306a36Sopenharmony_ci "copied %d bytes\n", len); 30262306a36Sopenharmony_ci break; 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci copy = min(len - copied, segment->size - segment->copied); 30662306a36Sopenharmony_ci ISCSI_DBG_TCP(tcp_conn->iscsi_conn, "copying %d\n", copy); 30762306a36Sopenharmony_ci memcpy(segment->data + segment->copied, ptr + copied, copy); 30862306a36Sopenharmony_ci copied += copy; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci return copied; 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ciinline void 31462306a36Sopenharmony_ciiscsi_tcp_dgst_header(struct ahash_request *hash, const void *hdr, 31562306a36Sopenharmony_ci size_t hdrlen, unsigned char digest[ISCSI_DIGEST_SIZE]) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct scatterlist sg; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci sg_init_one(&sg, hdr, hdrlen); 32062306a36Sopenharmony_ci ahash_request_set_crypt(hash, &sg, digest, hdrlen); 32162306a36Sopenharmony_ci crypto_ahash_digest(hash); 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_tcp_dgst_header); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic inline int 32662306a36Sopenharmony_ciiscsi_tcp_dgst_verify(struct iscsi_tcp_conn *tcp_conn, 32762306a36Sopenharmony_ci struct iscsi_segment *segment) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci if (!segment->digest_len) 33062306a36Sopenharmony_ci return 1; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (memcmp(segment->recv_digest, segment->digest, 33362306a36Sopenharmony_ci segment->digest_len)) { 33462306a36Sopenharmony_ci ISCSI_DBG_TCP(tcp_conn->iscsi_conn, "digest mismatch\n"); 33562306a36Sopenharmony_ci return 0; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return 1; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci/* 34262306a36Sopenharmony_ci * Helper function to set up segment buffer 34362306a36Sopenharmony_ci */ 34462306a36Sopenharmony_cistatic inline void 34562306a36Sopenharmony_ci__iscsi_segment_init(struct iscsi_segment *segment, size_t size, 34662306a36Sopenharmony_ci iscsi_segment_done_fn_t *done, struct ahash_request *hash) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci memset(segment, 0, sizeof(*segment)); 34962306a36Sopenharmony_ci segment->total_size = size; 35062306a36Sopenharmony_ci segment->done = done; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (hash) { 35362306a36Sopenharmony_ci segment->hash = hash; 35462306a36Sopenharmony_ci crypto_ahash_init(hash); 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ciinline void 35962306a36Sopenharmony_ciiscsi_segment_init_linear(struct iscsi_segment *segment, void *data, 36062306a36Sopenharmony_ci size_t size, iscsi_segment_done_fn_t *done, 36162306a36Sopenharmony_ci struct ahash_request *hash) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci __iscsi_segment_init(segment, size, done, hash); 36462306a36Sopenharmony_ci segment->data = data; 36562306a36Sopenharmony_ci segment->size = size; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_segment_init_linear); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ciinline int 37062306a36Sopenharmony_ciiscsi_segment_seek_sg(struct iscsi_segment *segment, 37162306a36Sopenharmony_ci struct scatterlist *sg_list, unsigned int sg_count, 37262306a36Sopenharmony_ci unsigned int offset, size_t size, 37362306a36Sopenharmony_ci iscsi_segment_done_fn_t *done, 37462306a36Sopenharmony_ci struct ahash_request *hash) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct scatterlist *sg; 37762306a36Sopenharmony_ci unsigned int i; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci __iscsi_segment_init(segment, size, done, hash); 38062306a36Sopenharmony_ci for_each_sg(sg_list, sg, sg_count, i) { 38162306a36Sopenharmony_ci if (offset < sg->length) { 38262306a36Sopenharmony_ci iscsi_tcp_segment_init_sg(segment, sg, offset); 38362306a36Sopenharmony_ci return 0; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci offset -= sg->length; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return ISCSI_ERR_DATA_OFFSET; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_segment_seek_sg); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci/** 39362306a36Sopenharmony_ci * iscsi_tcp_hdr_recv_prep - prep segment for hdr reception 39462306a36Sopenharmony_ci * @tcp_conn: iscsi connection to prep for 39562306a36Sopenharmony_ci * 39662306a36Sopenharmony_ci * This function always passes NULL for the hash argument, because when this 39762306a36Sopenharmony_ci * function is called we do not yet know the final size of the header and want 39862306a36Sopenharmony_ci * to delay the digest processing until we know that. 39962306a36Sopenharmony_ci */ 40062306a36Sopenharmony_civoid iscsi_tcp_hdr_recv_prep(struct iscsi_tcp_conn *tcp_conn) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci ISCSI_DBG_TCP(tcp_conn->iscsi_conn, 40362306a36Sopenharmony_ci "(%s)\n", tcp_conn->iscsi_conn->hdrdgst_en ? 40462306a36Sopenharmony_ci "digest enabled" : "digest disabled"); 40562306a36Sopenharmony_ci iscsi_segment_init_linear(&tcp_conn->in.segment, 40662306a36Sopenharmony_ci tcp_conn->in.hdr_buf, sizeof(struct iscsi_hdr), 40762306a36Sopenharmony_ci iscsi_tcp_hdr_recv_done, NULL); 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_tcp_hdr_recv_prep); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci/* 41262306a36Sopenharmony_ci * Handle incoming reply to any other type of command 41362306a36Sopenharmony_ci */ 41462306a36Sopenharmony_cistatic int 41562306a36Sopenharmony_ciiscsi_tcp_data_recv_done(struct iscsi_tcp_conn *tcp_conn, 41662306a36Sopenharmony_ci struct iscsi_segment *segment) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci struct iscsi_conn *conn = tcp_conn->iscsi_conn; 41962306a36Sopenharmony_ci int rc = 0; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (!iscsi_tcp_dgst_verify(tcp_conn, segment)) 42262306a36Sopenharmony_ci return ISCSI_ERR_DATA_DGST; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr, 42562306a36Sopenharmony_ci conn->data, tcp_conn->in.datalen); 42662306a36Sopenharmony_ci if (rc) 42762306a36Sopenharmony_ci return rc; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci iscsi_tcp_hdr_recv_prep(tcp_conn); 43062306a36Sopenharmony_ci return 0; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic void 43462306a36Sopenharmony_ciiscsi_tcp_data_recv_prep(struct iscsi_tcp_conn *tcp_conn) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct iscsi_conn *conn = tcp_conn->iscsi_conn; 43762306a36Sopenharmony_ci struct ahash_request *rx_hash = NULL; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (conn->datadgst_en && 44062306a36Sopenharmony_ci !(conn->session->tt->caps & CAP_DIGEST_OFFLOAD)) 44162306a36Sopenharmony_ci rx_hash = tcp_conn->rx_hash; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci iscsi_segment_init_linear(&tcp_conn->in.segment, 44462306a36Sopenharmony_ci conn->data, tcp_conn->in.datalen, 44562306a36Sopenharmony_ci iscsi_tcp_data_recv_done, rx_hash); 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci/** 44962306a36Sopenharmony_ci * iscsi_tcp_cleanup_task - free tcp_task resources 45062306a36Sopenharmony_ci * @task: iscsi task 45162306a36Sopenharmony_ci * 45262306a36Sopenharmony_ci * must be called with session back_lock 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_civoid iscsi_tcp_cleanup_task(struct iscsi_task *task) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct iscsi_tcp_task *tcp_task = task->dd_data; 45762306a36Sopenharmony_ci struct iscsi_r2t_info *r2t; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci /* nothing to do for mgmt */ 46062306a36Sopenharmony_ci if (!task->sc) 46162306a36Sopenharmony_ci return; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci spin_lock_bh(&tcp_task->queue2pool); 46462306a36Sopenharmony_ci /* flush task's r2t queues */ 46562306a36Sopenharmony_ci while (kfifo_out(&tcp_task->r2tqueue, (void*)&r2t, sizeof(void*))) { 46662306a36Sopenharmony_ci kfifo_in(&tcp_task->r2tpool.queue, (void*)&r2t, 46762306a36Sopenharmony_ci sizeof(void*)); 46862306a36Sopenharmony_ci ISCSI_DBG_TCP(task->conn, "pending r2t dropped\n"); 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci r2t = tcp_task->r2t; 47262306a36Sopenharmony_ci if (r2t != NULL) { 47362306a36Sopenharmony_ci kfifo_in(&tcp_task->r2tpool.queue, (void*)&r2t, 47462306a36Sopenharmony_ci sizeof(void*)); 47562306a36Sopenharmony_ci tcp_task->r2t = NULL; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci spin_unlock_bh(&tcp_task->queue2pool); 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_tcp_cleanup_task); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci/** 48262306a36Sopenharmony_ci * iscsi_tcp_data_in - SCSI Data-In Response processing 48362306a36Sopenharmony_ci * @conn: iscsi connection 48462306a36Sopenharmony_ci * @task: scsi command task 48562306a36Sopenharmony_ci */ 48662306a36Sopenharmony_cistatic int iscsi_tcp_data_in(struct iscsi_conn *conn, struct iscsi_task *task) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 48962306a36Sopenharmony_ci struct iscsi_tcp_task *tcp_task = task->dd_data; 49062306a36Sopenharmony_ci struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)tcp_conn->in.hdr; 49162306a36Sopenharmony_ci int datasn = be32_to_cpu(rhdr->datasn); 49262306a36Sopenharmony_ci unsigned total_in_length = task->sc->sdb.length; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* 49562306a36Sopenharmony_ci * lib iscsi will update this in the completion handling if there 49662306a36Sopenharmony_ci * is status. 49762306a36Sopenharmony_ci */ 49862306a36Sopenharmony_ci if (!(rhdr->flags & ISCSI_FLAG_DATA_STATUS)) 49962306a36Sopenharmony_ci iscsi_update_cmdsn(conn->session, (struct iscsi_nopin*)rhdr); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (tcp_conn->in.datalen == 0) 50262306a36Sopenharmony_ci return 0; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (tcp_task->exp_datasn != datasn) { 50562306a36Sopenharmony_ci ISCSI_DBG_TCP(conn, "task->exp_datasn(%d) != rhdr->datasn(%d)" 50662306a36Sopenharmony_ci "\n", tcp_task->exp_datasn, datasn); 50762306a36Sopenharmony_ci return ISCSI_ERR_DATASN; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci tcp_task->exp_datasn++; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci tcp_task->data_offset = be32_to_cpu(rhdr->offset); 51362306a36Sopenharmony_ci if (tcp_task->data_offset + tcp_conn->in.datalen > total_in_length) { 51462306a36Sopenharmony_ci ISCSI_DBG_TCP(conn, "data_offset(%d) + data_len(%d) > " 51562306a36Sopenharmony_ci "total_length_in(%d)\n", tcp_task->data_offset, 51662306a36Sopenharmony_ci tcp_conn->in.datalen, total_in_length); 51762306a36Sopenharmony_ci return ISCSI_ERR_DATA_OFFSET; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci conn->datain_pdus_cnt++; 52162306a36Sopenharmony_ci return 0; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci/** 52562306a36Sopenharmony_ci * iscsi_tcp_r2t_rsp - iSCSI R2T Response processing 52662306a36Sopenharmony_ci * @conn: iscsi connection 52762306a36Sopenharmony_ci * @hdr: PDU header 52862306a36Sopenharmony_ci */ 52962306a36Sopenharmony_cistatic int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 53262306a36Sopenharmony_ci struct iscsi_tcp_task *tcp_task; 53362306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn; 53462306a36Sopenharmony_ci struct iscsi_r2t_rsp *rhdr; 53562306a36Sopenharmony_ci struct iscsi_r2t_info *r2t; 53662306a36Sopenharmony_ci struct iscsi_task *task; 53762306a36Sopenharmony_ci u32 data_length; 53862306a36Sopenharmony_ci u32 data_offset; 53962306a36Sopenharmony_ci int r2tsn; 54062306a36Sopenharmony_ci int rc; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci spin_lock(&session->back_lock); 54362306a36Sopenharmony_ci task = iscsi_itt_to_ctask(conn, hdr->itt); 54462306a36Sopenharmony_ci if (!task) { 54562306a36Sopenharmony_ci spin_unlock(&session->back_lock); 54662306a36Sopenharmony_ci return ISCSI_ERR_BAD_ITT; 54762306a36Sopenharmony_ci } else if (task->sc->sc_data_direction != DMA_TO_DEVICE) { 54862306a36Sopenharmony_ci spin_unlock(&session->back_lock); 54962306a36Sopenharmony_ci return ISCSI_ERR_PROTO; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci /* 55262306a36Sopenharmony_ci * A bad target might complete the cmd before we have handled R2Ts 55362306a36Sopenharmony_ci * so get a ref to the task that will be dropped in the xmit path. 55462306a36Sopenharmony_ci */ 55562306a36Sopenharmony_ci if (task->state != ISCSI_TASK_RUNNING) { 55662306a36Sopenharmony_ci spin_unlock(&session->back_lock); 55762306a36Sopenharmony_ci /* Let the path that got the early rsp complete it */ 55862306a36Sopenharmony_ci return 0; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci task->last_xfer = jiffies; 56162306a36Sopenharmony_ci if (!iscsi_get_task(task)) { 56262306a36Sopenharmony_ci spin_unlock(&session->back_lock); 56362306a36Sopenharmony_ci /* Let the path that got the early rsp complete it */ 56462306a36Sopenharmony_ci return 0; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci tcp_conn = conn->dd_data; 56862306a36Sopenharmony_ci rhdr = (struct iscsi_r2t_rsp *)tcp_conn->in.hdr; 56962306a36Sopenharmony_ci /* fill-in new R2T associated with the task */ 57062306a36Sopenharmony_ci iscsi_update_cmdsn(session, (struct iscsi_nopin *)rhdr); 57162306a36Sopenharmony_ci spin_unlock(&session->back_lock); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (tcp_conn->in.datalen) { 57462306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 57562306a36Sopenharmony_ci "invalid R2t with datalen %d\n", 57662306a36Sopenharmony_ci tcp_conn->in.datalen); 57762306a36Sopenharmony_ci rc = ISCSI_ERR_DATALEN; 57862306a36Sopenharmony_ci goto put_task; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci tcp_task = task->dd_data; 58262306a36Sopenharmony_ci r2tsn = be32_to_cpu(rhdr->r2tsn); 58362306a36Sopenharmony_ci if (tcp_task->exp_datasn != r2tsn){ 58462306a36Sopenharmony_ci ISCSI_DBG_TCP(conn, "task->exp_datasn(%d) != rhdr->r2tsn(%d)\n", 58562306a36Sopenharmony_ci tcp_task->exp_datasn, r2tsn); 58662306a36Sopenharmony_ci rc = ISCSI_ERR_R2TSN; 58762306a36Sopenharmony_ci goto put_task; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (session->state != ISCSI_STATE_LOGGED_IN) { 59162306a36Sopenharmony_ci iscsi_conn_printk(KERN_INFO, conn, 59262306a36Sopenharmony_ci "dropping R2T itt %d in recovery.\n", 59362306a36Sopenharmony_ci task->itt); 59462306a36Sopenharmony_ci rc = 0; 59562306a36Sopenharmony_ci goto put_task; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci data_length = be32_to_cpu(rhdr->data_length); 59962306a36Sopenharmony_ci if (data_length == 0) { 60062306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 60162306a36Sopenharmony_ci "invalid R2T with zero data len\n"); 60262306a36Sopenharmony_ci rc = ISCSI_ERR_DATALEN; 60362306a36Sopenharmony_ci goto put_task; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci if (data_length > session->max_burst) 60762306a36Sopenharmony_ci ISCSI_DBG_TCP(conn, "invalid R2T with data len %u and max " 60862306a36Sopenharmony_ci "burst %u. Attempting to execute request.\n", 60962306a36Sopenharmony_ci data_length, session->max_burst); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci data_offset = be32_to_cpu(rhdr->data_offset); 61262306a36Sopenharmony_ci if (data_offset + data_length > task->sc->sdb.length) { 61362306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 61462306a36Sopenharmony_ci "invalid R2T with data len %u at offset %u " 61562306a36Sopenharmony_ci "and total length %d\n", data_length, 61662306a36Sopenharmony_ci data_offset, task->sc->sdb.length); 61762306a36Sopenharmony_ci rc = ISCSI_ERR_DATALEN; 61862306a36Sopenharmony_ci goto put_task; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci spin_lock(&tcp_task->pool2queue); 62262306a36Sopenharmony_ci rc = kfifo_out(&tcp_task->r2tpool.queue, (void *)&r2t, sizeof(void *)); 62362306a36Sopenharmony_ci if (!rc) { 62462306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, "Could not allocate R2T. " 62562306a36Sopenharmony_ci "Target has sent more R2Ts than it " 62662306a36Sopenharmony_ci "negotiated for or driver has leaked.\n"); 62762306a36Sopenharmony_ci spin_unlock(&tcp_task->pool2queue); 62862306a36Sopenharmony_ci rc = ISCSI_ERR_PROTO; 62962306a36Sopenharmony_ci goto put_task; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci r2t->exp_statsn = rhdr->statsn; 63362306a36Sopenharmony_ci r2t->data_length = data_length; 63462306a36Sopenharmony_ci r2t->data_offset = data_offset; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci r2t->ttt = rhdr->ttt; /* no flip */ 63762306a36Sopenharmony_ci r2t->datasn = 0; 63862306a36Sopenharmony_ci r2t->sent = 0; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci tcp_task->exp_datasn = r2tsn + 1; 64162306a36Sopenharmony_ci kfifo_in(&tcp_task->r2tqueue, (void*)&r2t, sizeof(void*)); 64262306a36Sopenharmony_ci conn->r2t_pdus_cnt++; 64362306a36Sopenharmony_ci spin_unlock(&tcp_task->pool2queue); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci iscsi_requeue_task(task); 64662306a36Sopenharmony_ci return 0; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ciput_task: 64962306a36Sopenharmony_ci iscsi_put_task(task); 65062306a36Sopenharmony_ci return rc; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci/* 65462306a36Sopenharmony_ci * Handle incoming reply to DataIn command 65562306a36Sopenharmony_ci */ 65662306a36Sopenharmony_cistatic int 65762306a36Sopenharmony_ciiscsi_tcp_process_data_in(struct iscsi_tcp_conn *tcp_conn, 65862306a36Sopenharmony_ci struct iscsi_segment *segment) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci struct iscsi_conn *conn = tcp_conn->iscsi_conn; 66162306a36Sopenharmony_ci struct iscsi_hdr *hdr = tcp_conn->in.hdr; 66262306a36Sopenharmony_ci int rc; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci if (!iscsi_tcp_dgst_verify(tcp_conn, segment)) 66562306a36Sopenharmony_ci return ISCSI_ERR_DATA_DGST; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci /* check for non-exceptional status */ 66862306a36Sopenharmony_ci if (hdr->flags & ISCSI_FLAG_DATA_STATUS) { 66962306a36Sopenharmony_ci rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr, NULL, 0); 67062306a36Sopenharmony_ci if (rc) 67162306a36Sopenharmony_ci return rc; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci iscsi_tcp_hdr_recv_prep(tcp_conn); 67562306a36Sopenharmony_ci return 0; 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci/** 67962306a36Sopenharmony_ci * iscsi_tcp_hdr_dissect - process PDU header 68062306a36Sopenharmony_ci * @conn: iSCSI connection 68162306a36Sopenharmony_ci * @hdr: PDU header 68262306a36Sopenharmony_ci * 68362306a36Sopenharmony_ci * This function analyzes the header of the PDU received, 68462306a36Sopenharmony_ci * and performs several sanity checks. If the PDU is accompanied 68562306a36Sopenharmony_ci * by data, the receive buffer is set up to copy the incoming data 68662306a36Sopenharmony_ci * to the correct location. 68762306a36Sopenharmony_ci */ 68862306a36Sopenharmony_cistatic int 68962306a36Sopenharmony_ciiscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci int rc = 0, opcode, ahslen; 69262306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 69362306a36Sopenharmony_ci struct iscsi_task *task; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci /* verify PDU length */ 69662306a36Sopenharmony_ci tcp_conn->in.datalen = ntoh24(hdr->dlength); 69762306a36Sopenharmony_ci if (tcp_conn->in.datalen > conn->max_recv_dlength) { 69862306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 69962306a36Sopenharmony_ci "iscsi_tcp: datalen %d > %d\n", 70062306a36Sopenharmony_ci tcp_conn->in.datalen, conn->max_recv_dlength); 70162306a36Sopenharmony_ci return ISCSI_ERR_DATALEN; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci /* Additional header segments. So far, we don't 70562306a36Sopenharmony_ci * process additional headers. 70662306a36Sopenharmony_ci */ 70762306a36Sopenharmony_ci ahslen = hdr->hlength << 2; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci opcode = hdr->opcode & ISCSI_OPCODE_MASK; 71062306a36Sopenharmony_ci /* verify itt (itt encoding: age+cid+itt) */ 71162306a36Sopenharmony_ci rc = iscsi_verify_itt(conn, hdr->itt); 71262306a36Sopenharmony_ci if (rc) 71362306a36Sopenharmony_ci return rc; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci ISCSI_DBG_TCP(conn, "opcode 0x%x ahslen %d datalen %d\n", 71662306a36Sopenharmony_ci opcode, ahslen, tcp_conn->in.datalen); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci switch(opcode) { 71962306a36Sopenharmony_ci case ISCSI_OP_SCSI_DATA_IN: 72062306a36Sopenharmony_ci spin_lock(&conn->session->back_lock); 72162306a36Sopenharmony_ci task = iscsi_itt_to_ctask(conn, hdr->itt); 72262306a36Sopenharmony_ci if (!task) 72362306a36Sopenharmony_ci rc = ISCSI_ERR_BAD_ITT; 72462306a36Sopenharmony_ci else 72562306a36Sopenharmony_ci rc = iscsi_tcp_data_in(conn, task); 72662306a36Sopenharmony_ci if (rc) { 72762306a36Sopenharmony_ci spin_unlock(&conn->session->back_lock); 72862306a36Sopenharmony_ci break; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci if (tcp_conn->in.datalen) { 73262306a36Sopenharmony_ci struct iscsi_tcp_task *tcp_task = task->dd_data; 73362306a36Sopenharmony_ci struct ahash_request *rx_hash = NULL; 73462306a36Sopenharmony_ci struct scsi_data_buffer *sdb = &task->sc->sdb; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci /* 73762306a36Sopenharmony_ci * Setup copy of Data-In into the struct scsi_cmnd 73862306a36Sopenharmony_ci * Scatterlist case: 73962306a36Sopenharmony_ci * We set up the iscsi_segment to point to the next 74062306a36Sopenharmony_ci * scatterlist entry to copy to. As we go along, 74162306a36Sopenharmony_ci * we move on to the next scatterlist entry and 74262306a36Sopenharmony_ci * update the digest per-entry. 74362306a36Sopenharmony_ci */ 74462306a36Sopenharmony_ci if (conn->datadgst_en && 74562306a36Sopenharmony_ci !(conn->session->tt->caps & CAP_DIGEST_OFFLOAD)) 74662306a36Sopenharmony_ci rx_hash = tcp_conn->rx_hash; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci ISCSI_DBG_TCP(conn, "iscsi_tcp_begin_data_in( " 74962306a36Sopenharmony_ci "offset=%d, datalen=%d)\n", 75062306a36Sopenharmony_ci tcp_task->data_offset, 75162306a36Sopenharmony_ci tcp_conn->in.datalen); 75262306a36Sopenharmony_ci task->last_xfer = jiffies; 75362306a36Sopenharmony_ci rc = iscsi_segment_seek_sg(&tcp_conn->in.segment, 75462306a36Sopenharmony_ci sdb->table.sgl, 75562306a36Sopenharmony_ci sdb->table.nents, 75662306a36Sopenharmony_ci tcp_task->data_offset, 75762306a36Sopenharmony_ci tcp_conn->in.datalen, 75862306a36Sopenharmony_ci iscsi_tcp_process_data_in, 75962306a36Sopenharmony_ci rx_hash); 76062306a36Sopenharmony_ci spin_unlock(&conn->session->back_lock); 76162306a36Sopenharmony_ci return rc; 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci rc = __iscsi_complete_pdu(conn, hdr, NULL, 0); 76462306a36Sopenharmony_ci spin_unlock(&conn->session->back_lock); 76562306a36Sopenharmony_ci break; 76662306a36Sopenharmony_ci case ISCSI_OP_SCSI_CMD_RSP: 76762306a36Sopenharmony_ci if (tcp_conn->in.datalen) { 76862306a36Sopenharmony_ci iscsi_tcp_data_recv_prep(tcp_conn); 76962306a36Sopenharmony_ci return 0; 77062306a36Sopenharmony_ci } 77162306a36Sopenharmony_ci rc = iscsi_complete_pdu(conn, hdr, NULL, 0); 77262306a36Sopenharmony_ci break; 77362306a36Sopenharmony_ci case ISCSI_OP_R2T: 77462306a36Sopenharmony_ci if (ahslen) { 77562306a36Sopenharmony_ci rc = ISCSI_ERR_AHSLEN; 77662306a36Sopenharmony_ci break; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci rc = iscsi_tcp_r2t_rsp(conn, hdr); 77962306a36Sopenharmony_ci break; 78062306a36Sopenharmony_ci case ISCSI_OP_LOGIN_RSP: 78162306a36Sopenharmony_ci case ISCSI_OP_TEXT_RSP: 78262306a36Sopenharmony_ci case ISCSI_OP_REJECT: 78362306a36Sopenharmony_ci case ISCSI_OP_ASYNC_EVENT: 78462306a36Sopenharmony_ci /* 78562306a36Sopenharmony_ci * It is possible that we could get a PDU with a buffer larger 78662306a36Sopenharmony_ci * than 8K, but there are no targets that currently do this. 78762306a36Sopenharmony_ci * For now we fail until we find a vendor that needs it 78862306a36Sopenharmony_ci */ 78962306a36Sopenharmony_ci if (ISCSI_DEF_MAX_RECV_SEG_LEN < tcp_conn->in.datalen) { 79062306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 79162306a36Sopenharmony_ci "iscsi_tcp: received buffer of " 79262306a36Sopenharmony_ci "len %u but conn buffer is only %u " 79362306a36Sopenharmony_ci "(opcode %0x)\n", 79462306a36Sopenharmony_ci tcp_conn->in.datalen, 79562306a36Sopenharmony_ci ISCSI_DEF_MAX_RECV_SEG_LEN, opcode); 79662306a36Sopenharmony_ci rc = ISCSI_ERR_PROTO; 79762306a36Sopenharmony_ci break; 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci /* If there's data coming in with the response, 80162306a36Sopenharmony_ci * receive it to the connection's buffer. 80262306a36Sopenharmony_ci */ 80362306a36Sopenharmony_ci if (tcp_conn->in.datalen) { 80462306a36Sopenharmony_ci iscsi_tcp_data_recv_prep(tcp_conn); 80562306a36Sopenharmony_ci return 0; 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci fallthrough; 80862306a36Sopenharmony_ci case ISCSI_OP_LOGOUT_RSP: 80962306a36Sopenharmony_ci case ISCSI_OP_NOOP_IN: 81062306a36Sopenharmony_ci case ISCSI_OP_SCSI_TMFUNC_RSP: 81162306a36Sopenharmony_ci rc = iscsi_complete_pdu(conn, hdr, NULL, 0); 81262306a36Sopenharmony_ci break; 81362306a36Sopenharmony_ci default: 81462306a36Sopenharmony_ci rc = ISCSI_ERR_BAD_OPCODE; 81562306a36Sopenharmony_ci break; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci if (rc == 0) { 81962306a36Sopenharmony_ci /* Anything that comes with data should have 82062306a36Sopenharmony_ci * been handled above. */ 82162306a36Sopenharmony_ci if (tcp_conn->in.datalen) 82262306a36Sopenharmony_ci return ISCSI_ERR_PROTO; 82362306a36Sopenharmony_ci iscsi_tcp_hdr_recv_prep(tcp_conn); 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci return rc; 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci/** 83062306a36Sopenharmony_ci * iscsi_tcp_hdr_recv_done - process PDU header 83162306a36Sopenharmony_ci * @tcp_conn: iSCSI TCP connection 83262306a36Sopenharmony_ci * @segment: the buffer segment being processed 83362306a36Sopenharmony_ci * 83462306a36Sopenharmony_ci * This is the callback invoked when the PDU header has 83562306a36Sopenharmony_ci * been received. If the header is followed by additional 83662306a36Sopenharmony_ci * header segments, we go back for more data. 83762306a36Sopenharmony_ci */ 83862306a36Sopenharmony_cistatic int 83962306a36Sopenharmony_ciiscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn, 84062306a36Sopenharmony_ci struct iscsi_segment *segment) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci struct iscsi_conn *conn = tcp_conn->iscsi_conn; 84362306a36Sopenharmony_ci struct iscsi_hdr *hdr; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci /* Check if there are additional header segments 84662306a36Sopenharmony_ci * *prior* to computing the digest, because we 84762306a36Sopenharmony_ci * may need to go back to the caller for more. 84862306a36Sopenharmony_ci */ 84962306a36Sopenharmony_ci hdr = (struct iscsi_hdr *) tcp_conn->in.hdr_buf; 85062306a36Sopenharmony_ci if (segment->copied == sizeof(struct iscsi_hdr) && hdr->hlength) { 85162306a36Sopenharmony_ci /* Bump the header length - the caller will 85262306a36Sopenharmony_ci * just loop around and get the AHS for us, and 85362306a36Sopenharmony_ci * call again. */ 85462306a36Sopenharmony_ci unsigned int ahslen = hdr->hlength << 2; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci /* Make sure we don't overflow */ 85762306a36Sopenharmony_ci if (sizeof(*hdr) + ahslen > sizeof(tcp_conn->in.hdr_buf)) 85862306a36Sopenharmony_ci return ISCSI_ERR_AHSLEN; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci segment->total_size += ahslen; 86162306a36Sopenharmony_ci segment->size += ahslen; 86262306a36Sopenharmony_ci return 0; 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci /* We're done processing the header. See if we're doing 86662306a36Sopenharmony_ci * header digests; if so, set up the recv_digest buffer 86762306a36Sopenharmony_ci * and go back for more. */ 86862306a36Sopenharmony_ci if (conn->hdrdgst_en && 86962306a36Sopenharmony_ci !(conn->session->tt->caps & CAP_DIGEST_OFFLOAD)) { 87062306a36Sopenharmony_ci if (segment->digest_len == 0) { 87162306a36Sopenharmony_ci /* 87262306a36Sopenharmony_ci * Even if we offload the digest processing we 87362306a36Sopenharmony_ci * splice it in so we can increment the skb/segment 87462306a36Sopenharmony_ci * counters in preparation for the data segment. 87562306a36Sopenharmony_ci */ 87662306a36Sopenharmony_ci iscsi_tcp_segment_splice_digest(segment, 87762306a36Sopenharmony_ci segment->recv_digest); 87862306a36Sopenharmony_ci return 0; 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci iscsi_tcp_dgst_header(tcp_conn->rx_hash, hdr, 88262306a36Sopenharmony_ci segment->total_copied - ISCSI_DIGEST_SIZE, 88362306a36Sopenharmony_ci segment->digest); 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci if (!iscsi_tcp_dgst_verify(tcp_conn, segment)) 88662306a36Sopenharmony_ci return ISCSI_ERR_HDR_DGST; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci tcp_conn->in.hdr = hdr; 89062306a36Sopenharmony_ci return iscsi_tcp_hdr_dissect(conn, hdr); 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci/** 89462306a36Sopenharmony_ci * iscsi_tcp_recv_segment_is_hdr - tests if we are reading in a header 89562306a36Sopenharmony_ci * @tcp_conn: iscsi tcp conn 89662306a36Sopenharmony_ci * 89762306a36Sopenharmony_ci * returns non zero if we are currently processing or setup to process 89862306a36Sopenharmony_ci * a header. 89962306a36Sopenharmony_ci */ 90062306a36Sopenharmony_ciinline int iscsi_tcp_recv_segment_is_hdr(struct iscsi_tcp_conn *tcp_conn) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci return tcp_conn->in.segment.done == iscsi_tcp_hdr_recv_done; 90362306a36Sopenharmony_ci} 90462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_tcp_recv_segment_is_hdr); 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci/** 90762306a36Sopenharmony_ci * iscsi_tcp_recv_skb - Process skb 90862306a36Sopenharmony_ci * @conn: iscsi connection 90962306a36Sopenharmony_ci * @skb: network buffer with header and/or data segment 91062306a36Sopenharmony_ci * @offset: offset in skb 91162306a36Sopenharmony_ci * @offloaded: bool indicating if transfer was offloaded 91262306a36Sopenharmony_ci * @status: iscsi TCP status result 91362306a36Sopenharmony_ci * 91462306a36Sopenharmony_ci * Will return status of transfer in @status. And will return 91562306a36Sopenharmony_ci * number of bytes copied. 91662306a36Sopenharmony_ci */ 91762306a36Sopenharmony_ciint iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb, 91862306a36Sopenharmony_ci unsigned int offset, bool offloaded, int *status) 91962306a36Sopenharmony_ci{ 92062306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 92162306a36Sopenharmony_ci struct iscsi_segment *segment = &tcp_conn->in.segment; 92262306a36Sopenharmony_ci struct skb_seq_state seq; 92362306a36Sopenharmony_ci unsigned int consumed = 0; 92462306a36Sopenharmony_ci int rc = 0; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci ISCSI_DBG_TCP(conn, "in %d bytes\n", skb->len - offset); 92762306a36Sopenharmony_ci /* 92862306a36Sopenharmony_ci * Update for each skb instead of pdu, because over slow networks a 92962306a36Sopenharmony_ci * data_in's data could take a while to read in. We also want to 93062306a36Sopenharmony_ci * account for r2ts. 93162306a36Sopenharmony_ci */ 93262306a36Sopenharmony_ci conn->last_recv = jiffies; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci if (unlikely(test_bit(ISCSI_CONN_FLAG_SUSPEND_RX, &conn->flags))) { 93562306a36Sopenharmony_ci ISCSI_DBG_TCP(conn, "Rx suspended!\n"); 93662306a36Sopenharmony_ci *status = ISCSI_TCP_SUSPENDED; 93762306a36Sopenharmony_ci return 0; 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci if (offloaded) { 94162306a36Sopenharmony_ci segment->total_copied = segment->total_size; 94262306a36Sopenharmony_ci goto segment_done; 94362306a36Sopenharmony_ci } 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci skb_prepare_seq_read(skb, offset, skb->len, &seq); 94662306a36Sopenharmony_ci while (1) { 94762306a36Sopenharmony_ci unsigned int avail; 94862306a36Sopenharmony_ci const u8 *ptr; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci avail = skb_seq_read(consumed, &ptr, &seq); 95162306a36Sopenharmony_ci if (avail == 0) { 95262306a36Sopenharmony_ci ISCSI_DBG_TCP(conn, "no more data avail. Consumed %d\n", 95362306a36Sopenharmony_ci consumed); 95462306a36Sopenharmony_ci *status = ISCSI_TCP_SKB_DONE; 95562306a36Sopenharmony_ci goto skb_done; 95662306a36Sopenharmony_ci } 95762306a36Sopenharmony_ci BUG_ON(segment->copied >= segment->size); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci ISCSI_DBG_TCP(conn, "skb %p ptr=%p avail=%u\n", skb, ptr, 96062306a36Sopenharmony_ci avail); 96162306a36Sopenharmony_ci rc = iscsi_tcp_segment_recv(tcp_conn, segment, ptr, avail); 96262306a36Sopenharmony_ci BUG_ON(rc == 0); 96362306a36Sopenharmony_ci consumed += rc; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci if (segment->total_copied >= segment->total_size) { 96662306a36Sopenharmony_ci skb_abort_seq_read(&seq); 96762306a36Sopenharmony_ci goto segment_done; 96862306a36Sopenharmony_ci } 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_cisegment_done: 97262306a36Sopenharmony_ci *status = ISCSI_TCP_SEGMENT_DONE; 97362306a36Sopenharmony_ci ISCSI_DBG_TCP(conn, "segment done\n"); 97462306a36Sopenharmony_ci rc = segment->done(tcp_conn, segment); 97562306a36Sopenharmony_ci if (rc != 0) { 97662306a36Sopenharmony_ci *status = ISCSI_TCP_CONN_ERR; 97762306a36Sopenharmony_ci ISCSI_DBG_TCP(conn, "Error receiving PDU, errno=%d\n", rc); 97862306a36Sopenharmony_ci iscsi_conn_failure(conn, rc); 97962306a36Sopenharmony_ci return 0; 98062306a36Sopenharmony_ci } 98162306a36Sopenharmony_ci /* The done() functions sets up the next segment. */ 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ciskb_done: 98462306a36Sopenharmony_ci conn->rxdata_octets += consumed; 98562306a36Sopenharmony_ci return consumed; 98662306a36Sopenharmony_ci} 98762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_tcp_recv_skb); 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci/** 99062306a36Sopenharmony_ci * iscsi_tcp_task_init - Initialize iSCSI SCSI_READ or SCSI_WRITE commands 99162306a36Sopenharmony_ci * @task: scsi command task 99262306a36Sopenharmony_ci */ 99362306a36Sopenharmony_ciint iscsi_tcp_task_init(struct iscsi_task *task) 99462306a36Sopenharmony_ci{ 99562306a36Sopenharmony_ci struct iscsi_tcp_task *tcp_task = task->dd_data; 99662306a36Sopenharmony_ci struct iscsi_conn *conn = task->conn; 99762306a36Sopenharmony_ci struct scsi_cmnd *sc = task->sc; 99862306a36Sopenharmony_ci int err; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci if (!sc) { 100162306a36Sopenharmony_ci /* 100262306a36Sopenharmony_ci * mgmt tasks do not have a scatterlist since they come 100362306a36Sopenharmony_ci * in from the iscsi interface. 100462306a36Sopenharmony_ci */ 100562306a36Sopenharmony_ci ISCSI_DBG_TCP(conn, "mtask deq [itt 0x%x]\n", task->itt); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci return conn->session->tt->init_pdu(task, 0, task->data_count); 100862306a36Sopenharmony_ci } 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci BUG_ON(kfifo_len(&tcp_task->r2tqueue)); 101162306a36Sopenharmony_ci tcp_task->exp_datasn = 0; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci /* Prepare PDU, optionally w/ immediate data */ 101462306a36Sopenharmony_ci ISCSI_DBG_TCP(conn, "task deq [itt 0x%x imm %d unsol %d]\n", 101562306a36Sopenharmony_ci task->itt, task->imm_count, task->unsol_r2t.data_length); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci err = conn->session->tt->init_pdu(task, 0, task->imm_count); 101862306a36Sopenharmony_ci if (err) 101962306a36Sopenharmony_ci return err; 102062306a36Sopenharmony_ci task->imm_count = 0; 102162306a36Sopenharmony_ci return 0; 102262306a36Sopenharmony_ci} 102362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_tcp_task_init); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_cistatic struct iscsi_r2t_info *iscsi_tcp_get_curr_r2t(struct iscsi_task *task) 102662306a36Sopenharmony_ci{ 102762306a36Sopenharmony_ci struct iscsi_tcp_task *tcp_task = task->dd_data; 102862306a36Sopenharmony_ci struct iscsi_r2t_info *r2t = NULL; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci if (iscsi_task_has_unsol_data(task)) 103162306a36Sopenharmony_ci r2t = &task->unsol_r2t; 103262306a36Sopenharmony_ci else { 103362306a36Sopenharmony_ci spin_lock_bh(&tcp_task->queue2pool); 103462306a36Sopenharmony_ci if (tcp_task->r2t) { 103562306a36Sopenharmony_ci r2t = tcp_task->r2t; 103662306a36Sopenharmony_ci /* Continue with this R2T? */ 103762306a36Sopenharmony_ci if (r2t->data_length <= r2t->sent) { 103862306a36Sopenharmony_ci ISCSI_DBG_TCP(task->conn, 103962306a36Sopenharmony_ci " done with r2t %p\n", r2t); 104062306a36Sopenharmony_ci kfifo_in(&tcp_task->r2tpool.queue, 104162306a36Sopenharmony_ci (void *)&tcp_task->r2t, 104262306a36Sopenharmony_ci sizeof(void *)); 104362306a36Sopenharmony_ci tcp_task->r2t = r2t = NULL; 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci if (r2t == NULL) { 104862306a36Sopenharmony_ci if (kfifo_out(&tcp_task->r2tqueue, 104962306a36Sopenharmony_ci (void *)&tcp_task->r2t, sizeof(void *)) != 105062306a36Sopenharmony_ci sizeof(void *)) 105162306a36Sopenharmony_ci r2t = NULL; 105262306a36Sopenharmony_ci else 105362306a36Sopenharmony_ci r2t = tcp_task->r2t; 105462306a36Sopenharmony_ci } 105562306a36Sopenharmony_ci spin_unlock_bh(&tcp_task->queue2pool); 105662306a36Sopenharmony_ci } 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci return r2t; 105962306a36Sopenharmony_ci} 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci/** 106262306a36Sopenharmony_ci * iscsi_tcp_task_xmit - xmit normal PDU task 106362306a36Sopenharmony_ci * @task: iscsi command task 106462306a36Sopenharmony_ci * 106562306a36Sopenharmony_ci * We're expected to return 0 when everything was transmitted successfully, 106662306a36Sopenharmony_ci * -EAGAIN if there's still data in the queue, or != 0 for any other kind 106762306a36Sopenharmony_ci * of error. 106862306a36Sopenharmony_ci */ 106962306a36Sopenharmony_ciint iscsi_tcp_task_xmit(struct iscsi_task *task) 107062306a36Sopenharmony_ci{ 107162306a36Sopenharmony_ci struct iscsi_conn *conn = task->conn; 107262306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 107362306a36Sopenharmony_ci struct iscsi_r2t_info *r2t; 107462306a36Sopenharmony_ci int rc = 0; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ciflush: 107762306a36Sopenharmony_ci /* Flush any pending data first. */ 107862306a36Sopenharmony_ci rc = session->tt->xmit_pdu(task); 107962306a36Sopenharmony_ci if (rc < 0) 108062306a36Sopenharmony_ci return rc; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci /* mgmt command */ 108362306a36Sopenharmony_ci if (!task->sc) { 108462306a36Sopenharmony_ci if (task->hdr->itt == RESERVED_ITT) 108562306a36Sopenharmony_ci iscsi_put_task(task); 108662306a36Sopenharmony_ci return 0; 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci /* Are we done already? */ 109062306a36Sopenharmony_ci if (task->sc->sc_data_direction != DMA_TO_DEVICE) 109162306a36Sopenharmony_ci return 0; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci r2t = iscsi_tcp_get_curr_r2t(task); 109462306a36Sopenharmony_ci if (r2t == NULL) { 109562306a36Sopenharmony_ci /* Waiting for more R2Ts to arrive. */ 109662306a36Sopenharmony_ci ISCSI_DBG_TCP(conn, "no R2Ts yet\n"); 109762306a36Sopenharmony_ci return 0; 109862306a36Sopenharmony_ci } 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci rc = conn->session->tt->alloc_pdu(task, ISCSI_OP_SCSI_DATA_OUT); 110162306a36Sopenharmony_ci if (rc) 110262306a36Sopenharmony_ci return rc; 110362306a36Sopenharmony_ci iscsi_prep_data_out_pdu(task, r2t, (struct iscsi_data *) task->hdr); 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci ISCSI_DBG_TCP(conn, "sol dout %p [dsn %d itt 0x%x doff %d dlen %d]\n", 110662306a36Sopenharmony_ci r2t, r2t->datasn - 1, task->hdr->itt, 110762306a36Sopenharmony_ci r2t->data_offset + r2t->sent, r2t->data_count); 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci rc = conn->session->tt->init_pdu(task, r2t->data_offset + r2t->sent, 111062306a36Sopenharmony_ci r2t->data_count); 111162306a36Sopenharmony_ci if (rc) { 111262306a36Sopenharmony_ci iscsi_conn_failure(conn, ISCSI_ERR_XMIT_FAILED); 111362306a36Sopenharmony_ci return rc; 111462306a36Sopenharmony_ci } 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci r2t->sent += r2t->data_count; 111762306a36Sopenharmony_ci goto flush; 111862306a36Sopenharmony_ci} 111962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_tcp_task_xmit); 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_cistruct iscsi_cls_conn * 112262306a36Sopenharmony_ciiscsi_tcp_conn_setup(struct iscsi_cls_session *cls_session, int dd_data_size, 112362306a36Sopenharmony_ci uint32_t conn_idx) 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci{ 112662306a36Sopenharmony_ci struct iscsi_conn *conn; 112762306a36Sopenharmony_ci struct iscsi_cls_conn *cls_conn; 112862306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci cls_conn = iscsi_conn_setup(cls_session, 113162306a36Sopenharmony_ci sizeof(*tcp_conn) + dd_data_size, conn_idx); 113262306a36Sopenharmony_ci if (!cls_conn) 113362306a36Sopenharmony_ci return NULL; 113462306a36Sopenharmony_ci conn = cls_conn->dd_data; 113562306a36Sopenharmony_ci /* 113662306a36Sopenharmony_ci * due to strange issues with iser these are not set 113762306a36Sopenharmony_ci * in iscsi_conn_setup 113862306a36Sopenharmony_ci */ 113962306a36Sopenharmony_ci conn->max_recv_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci tcp_conn = conn->dd_data; 114262306a36Sopenharmony_ci tcp_conn->iscsi_conn = conn; 114362306a36Sopenharmony_ci tcp_conn->dd_data = conn->dd_data + sizeof(*tcp_conn); 114462306a36Sopenharmony_ci return cls_conn; 114562306a36Sopenharmony_ci} 114662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_tcp_conn_setup); 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_civoid iscsi_tcp_conn_teardown(struct iscsi_cls_conn *cls_conn) 114962306a36Sopenharmony_ci{ 115062306a36Sopenharmony_ci iscsi_conn_teardown(cls_conn); 115162306a36Sopenharmony_ci} 115262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_tcp_conn_teardown); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ciint iscsi_tcp_r2tpool_alloc(struct iscsi_session *session) 115562306a36Sopenharmony_ci{ 115662306a36Sopenharmony_ci int i; 115762306a36Sopenharmony_ci int cmd_i; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci /* 116062306a36Sopenharmony_ci * initialize per-task: R2T pool and xmit queue 116162306a36Sopenharmony_ci */ 116262306a36Sopenharmony_ci for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) { 116362306a36Sopenharmony_ci struct iscsi_task *task = session->cmds[cmd_i]; 116462306a36Sopenharmony_ci struct iscsi_tcp_task *tcp_task = task->dd_data; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci /* 116762306a36Sopenharmony_ci * pre-allocated x2 as much r2ts to handle race when 116862306a36Sopenharmony_ci * target acks DataOut faster than we data_xmit() queues 116962306a36Sopenharmony_ci * could replenish r2tqueue. 117062306a36Sopenharmony_ci */ 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci /* R2T pool */ 117362306a36Sopenharmony_ci if (iscsi_pool_init(&tcp_task->r2tpool, 117462306a36Sopenharmony_ci session->max_r2t * 2, NULL, 117562306a36Sopenharmony_ci sizeof(struct iscsi_r2t_info))) { 117662306a36Sopenharmony_ci goto r2t_alloc_fail; 117762306a36Sopenharmony_ci } 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci /* R2T xmit queue */ 118062306a36Sopenharmony_ci if (kfifo_alloc(&tcp_task->r2tqueue, 118162306a36Sopenharmony_ci session->max_r2t * 4 * sizeof(void*), GFP_KERNEL)) { 118262306a36Sopenharmony_ci iscsi_pool_free(&tcp_task->r2tpool); 118362306a36Sopenharmony_ci goto r2t_alloc_fail; 118462306a36Sopenharmony_ci } 118562306a36Sopenharmony_ci spin_lock_init(&tcp_task->pool2queue); 118662306a36Sopenharmony_ci spin_lock_init(&tcp_task->queue2pool); 118762306a36Sopenharmony_ci } 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci return 0; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_cir2t_alloc_fail: 119262306a36Sopenharmony_ci for (i = 0; i < cmd_i; i++) { 119362306a36Sopenharmony_ci struct iscsi_task *task = session->cmds[i]; 119462306a36Sopenharmony_ci struct iscsi_tcp_task *tcp_task = task->dd_data; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci kfifo_free(&tcp_task->r2tqueue); 119762306a36Sopenharmony_ci iscsi_pool_free(&tcp_task->r2tpool); 119862306a36Sopenharmony_ci } 119962306a36Sopenharmony_ci return -ENOMEM; 120062306a36Sopenharmony_ci} 120162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_tcp_r2tpool_alloc); 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_civoid iscsi_tcp_r2tpool_free(struct iscsi_session *session) 120462306a36Sopenharmony_ci{ 120562306a36Sopenharmony_ci int i; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci for (i = 0; i < session->cmds_max; i++) { 120862306a36Sopenharmony_ci struct iscsi_task *task = session->cmds[i]; 120962306a36Sopenharmony_ci struct iscsi_tcp_task *tcp_task = task->dd_data; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci kfifo_free(&tcp_task->r2tqueue); 121262306a36Sopenharmony_ci iscsi_pool_free(&tcp_task->r2tpool); 121362306a36Sopenharmony_ci } 121462306a36Sopenharmony_ci} 121562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_tcp_r2tpool_free); 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ciint iscsi_tcp_set_max_r2t(struct iscsi_conn *conn, char *buf) 121862306a36Sopenharmony_ci{ 121962306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 122062306a36Sopenharmony_ci unsigned short r2ts = 0; 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci sscanf(buf, "%hu", &r2ts); 122362306a36Sopenharmony_ci if (session->max_r2t == r2ts) 122462306a36Sopenharmony_ci return 0; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci if (!r2ts || !is_power_of_2(r2ts)) 122762306a36Sopenharmony_ci return -EINVAL; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci session->max_r2t = r2ts; 123062306a36Sopenharmony_ci iscsi_tcp_r2tpool_free(session); 123162306a36Sopenharmony_ci return iscsi_tcp_r2tpool_alloc(session); 123262306a36Sopenharmony_ci} 123362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_tcp_set_max_r2t); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_civoid iscsi_tcp_conn_get_stats(struct iscsi_cls_conn *cls_conn, 123662306a36Sopenharmony_ci struct iscsi_stats *stats) 123762306a36Sopenharmony_ci{ 123862306a36Sopenharmony_ci struct iscsi_conn *conn = cls_conn->dd_data; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci stats->txdata_octets = conn->txdata_octets; 124162306a36Sopenharmony_ci stats->rxdata_octets = conn->rxdata_octets; 124262306a36Sopenharmony_ci stats->scsicmd_pdus = conn->scsicmd_pdus_cnt; 124362306a36Sopenharmony_ci stats->dataout_pdus = conn->dataout_pdus_cnt; 124462306a36Sopenharmony_ci stats->scsirsp_pdus = conn->scsirsp_pdus_cnt; 124562306a36Sopenharmony_ci stats->datain_pdus = conn->datain_pdus_cnt; 124662306a36Sopenharmony_ci stats->r2t_pdus = conn->r2t_pdus_cnt; 124762306a36Sopenharmony_ci stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt; 124862306a36Sopenharmony_ci stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt; 124962306a36Sopenharmony_ci} 125062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_tcp_conn_get_stats); 1251