18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * iSCSI Initiator over TCP/IP Data-Path
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2004 Dmitry Yusupov
68c2ecf20Sopenharmony_ci * Copyright (C) 2004 Alex Aizman
78c2ecf20Sopenharmony_ci * Copyright (C) 2005 - 2006 Mike Christie
88c2ecf20Sopenharmony_ci * Copyright (C) 2006 Red Hat, Inc.  All rights reserved.
98c2ecf20Sopenharmony_ci * maintained by open-iscsi@googlegroups.com
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * See the file COPYING included with this distribution for more details.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * Credits:
148c2ecf20Sopenharmony_ci *	Christoph Hellwig
158c2ecf20Sopenharmony_ci *	FUJITA Tomonori
168c2ecf20Sopenharmony_ci *	Arne Redlich
178c2ecf20Sopenharmony_ci *	Zhenyu Wang
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <crypto/hash.h>
218c2ecf20Sopenharmony_ci#include <linux/types.h>
228c2ecf20Sopenharmony_ci#include <linux/inet.h>
238c2ecf20Sopenharmony_ci#include <linux/slab.h>
248c2ecf20Sopenharmony_ci#include <linux/sched/mm.h>
258c2ecf20Sopenharmony_ci#include <linux/file.h>
268c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
278c2ecf20Sopenharmony_ci#include <linux/delay.h>
288c2ecf20Sopenharmony_ci#include <linux/kfifo.h>
298c2ecf20Sopenharmony_ci#include <linux/scatterlist.h>
308c2ecf20Sopenharmony_ci#include <linux/module.h>
318c2ecf20Sopenharmony_ci#include <linux/backing-dev.h>
328c2ecf20Sopenharmony_ci#include <net/tcp.h>
338c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h>
348c2ecf20Sopenharmony_ci#include <scsi/scsi_device.h>
358c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
368c2ecf20Sopenharmony_ci#include <scsi/scsi.h>
378c2ecf20Sopenharmony_ci#include <scsi/scsi_transport_iscsi.h>
388c2ecf20Sopenharmony_ci#include <trace/events/iscsi.h>
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#include "iscsi_tcp.h"
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, "
438c2ecf20Sopenharmony_ci	      "Dmitry Yusupov <dmitry_yus@yahoo.com>, "
448c2ecf20Sopenharmony_ci	      "Alex Aizman <itn780@yahoo.com>");
458c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("iSCSI/TCP data-path");
468c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic struct scsi_transport_template *iscsi_sw_tcp_scsi_transport;
498c2ecf20Sopenharmony_cistatic struct scsi_host_template iscsi_sw_tcp_sht;
508c2ecf20Sopenharmony_cistatic struct iscsi_transport iscsi_sw_tcp_transport;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic unsigned int iscsi_max_lun = ~0;
538c2ecf20Sopenharmony_cimodule_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic int iscsi_sw_tcp_dbg;
568c2ecf20Sopenharmony_cimodule_param_named(debug_iscsi_tcp, iscsi_sw_tcp_dbg, int,
578c2ecf20Sopenharmony_ci		   S_IRUGO | S_IWUSR);
588c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug_iscsi_tcp, "Turn on debugging for iscsi_tcp module "
598c2ecf20Sopenharmony_ci		 "Set to 1 to turn on, and zero to turn off. Default is off.");
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#define ISCSI_SW_TCP_DBG(_conn, dbg_fmt, arg...)		\
628c2ecf20Sopenharmony_ci	do {							\
638c2ecf20Sopenharmony_ci		if (iscsi_sw_tcp_dbg)				\
648c2ecf20Sopenharmony_ci			iscsi_conn_printk(KERN_INFO, _conn,	\
658c2ecf20Sopenharmony_ci					     "%s " dbg_fmt,	\
668c2ecf20Sopenharmony_ci					     __func__, ##arg);	\
678c2ecf20Sopenharmony_ci		iscsi_dbg_trace(trace_iscsi_dbg_sw_tcp,		\
688c2ecf20Sopenharmony_ci				&(_conn)->cls_conn->dev,	\
698c2ecf20Sopenharmony_ci				"%s " dbg_fmt, __func__, ##arg);\
708c2ecf20Sopenharmony_ci	} while (0);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci/**
748c2ecf20Sopenharmony_ci * iscsi_sw_tcp_recv - TCP receive in sendfile fashion
758c2ecf20Sopenharmony_ci * @rd_desc: read descriptor
768c2ecf20Sopenharmony_ci * @skb: socket buffer
778c2ecf20Sopenharmony_ci * @offset: offset in skb
788c2ecf20Sopenharmony_ci * @len: skb->len - offset
798c2ecf20Sopenharmony_ci */
808c2ecf20Sopenharmony_cistatic int iscsi_sw_tcp_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,
818c2ecf20Sopenharmony_ci			     unsigned int offset, size_t len)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = rd_desc->arg.data;
848c2ecf20Sopenharmony_ci	unsigned int consumed, total_consumed = 0;
858c2ecf20Sopenharmony_ci	int status;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	ISCSI_SW_TCP_DBG(conn, "in %d bytes\n", skb->len - offset);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	do {
908c2ecf20Sopenharmony_ci		status = 0;
918c2ecf20Sopenharmony_ci		consumed = iscsi_tcp_recv_skb(conn, skb, offset, 0, &status);
928c2ecf20Sopenharmony_ci		offset += consumed;
938c2ecf20Sopenharmony_ci		total_consumed += consumed;
948c2ecf20Sopenharmony_ci	} while (consumed != 0 && status != ISCSI_TCP_SKB_DONE);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	ISCSI_SW_TCP_DBG(conn, "read %d bytes status %d\n",
978c2ecf20Sopenharmony_ci			 skb->len - offset, status);
988c2ecf20Sopenharmony_ci	return total_consumed;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/**
1028c2ecf20Sopenharmony_ci * iscsi_sw_sk_state_check - check socket state
1038c2ecf20Sopenharmony_ci * @sk: socket
1048c2ecf20Sopenharmony_ci *
1058c2ecf20Sopenharmony_ci * If the socket is in CLOSE or CLOSE_WAIT we should
1068c2ecf20Sopenharmony_ci * not close the connection if there is still some
1078c2ecf20Sopenharmony_ci * data pending.
1088c2ecf20Sopenharmony_ci *
1098c2ecf20Sopenharmony_ci * Must be called with sk_callback_lock.
1108c2ecf20Sopenharmony_ci */
1118c2ecf20Sopenharmony_cistatic inline int iscsi_sw_sk_state_check(struct sock *sk)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = sk->sk_user_data;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	if ((sk->sk_state == TCP_CLOSE_WAIT || sk->sk_state == TCP_CLOSE) &&
1168c2ecf20Sopenharmony_ci	    (conn->session->state != ISCSI_STATE_LOGGING_OUT) &&
1178c2ecf20Sopenharmony_ci	    !atomic_read(&sk->sk_rmem_alloc)) {
1188c2ecf20Sopenharmony_ci		ISCSI_SW_TCP_DBG(conn, "TCP_CLOSE|TCP_CLOSE_WAIT\n");
1198c2ecf20Sopenharmony_ci		iscsi_conn_failure(conn, ISCSI_ERR_TCP_CONN_CLOSE);
1208c2ecf20Sopenharmony_ci		return -ECONNRESET;
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci	return 0;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic void iscsi_sw_tcp_data_ready(struct sock *sk)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	struct iscsi_conn *conn;
1288c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn;
1298c2ecf20Sopenharmony_ci	read_descriptor_t rd_desc;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	read_lock_bh(&sk->sk_callback_lock);
1328c2ecf20Sopenharmony_ci	conn = sk->sk_user_data;
1338c2ecf20Sopenharmony_ci	if (!conn) {
1348c2ecf20Sopenharmony_ci		read_unlock_bh(&sk->sk_callback_lock);
1358c2ecf20Sopenharmony_ci		return;
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci	tcp_conn = conn->dd_data;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/*
1408c2ecf20Sopenharmony_ci	 * Use rd_desc to pass 'conn' to iscsi_tcp_recv.
1418c2ecf20Sopenharmony_ci	 * We set count to 1 because we want the network layer to
1428c2ecf20Sopenharmony_ci	 * hand us all the skbs that are available. iscsi_tcp_recv
1438c2ecf20Sopenharmony_ci	 * handled pdus that cross buffers or pdus that still need data.
1448c2ecf20Sopenharmony_ci	 */
1458c2ecf20Sopenharmony_ci	rd_desc.arg.data = conn;
1468c2ecf20Sopenharmony_ci	rd_desc.count = 1;
1478c2ecf20Sopenharmony_ci	tcp_read_sock(sk, &rd_desc, iscsi_sw_tcp_recv);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	iscsi_sw_sk_state_check(sk);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	/* If we had to (atomically) map a highmem page,
1528c2ecf20Sopenharmony_ci	 * unmap it now. */
1538c2ecf20Sopenharmony_ci	iscsi_tcp_segment_unmap(&tcp_conn->in.segment);
1548c2ecf20Sopenharmony_ci	read_unlock_bh(&sk->sk_callback_lock);
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic void iscsi_sw_tcp_state_change(struct sock *sk)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn;
1608c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_conn *tcp_sw_conn;
1618c2ecf20Sopenharmony_ci	struct iscsi_conn *conn;
1628c2ecf20Sopenharmony_ci	void (*old_state_change)(struct sock *);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	read_lock_bh(&sk->sk_callback_lock);
1658c2ecf20Sopenharmony_ci	conn = sk->sk_user_data;
1668c2ecf20Sopenharmony_ci	if (!conn) {
1678c2ecf20Sopenharmony_ci		read_unlock_bh(&sk->sk_callback_lock);
1688c2ecf20Sopenharmony_ci		return;
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	iscsi_sw_sk_state_check(sk);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	tcp_conn = conn->dd_data;
1748c2ecf20Sopenharmony_ci	tcp_sw_conn = tcp_conn->dd_data;
1758c2ecf20Sopenharmony_ci	old_state_change = tcp_sw_conn->old_state_change;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	read_unlock_bh(&sk->sk_callback_lock);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	old_state_change(sk);
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/**
1838c2ecf20Sopenharmony_ci * iscsi_write_space - Called when more output buffer space is available
1848c2ecf20Sopenharmony_ci * @sk: socket space is available for
1858c2ecf20Sopenharmony_ci **/
1868c2ecf20Sopenharmony_cistatic void iscsi_sw_tcp_write_space(struct sock *sk)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct iscsi_conn *conn;
1898c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn;
1908c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_conn *tcp_sw_conn;
1918c2ecf20Sopenharmony_ci	void (*old_write_space)(struct sock *);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	read_lock_bh(&sk->sk_callback_lock);
1948c2ecf20Sopenharmony_ci	conn = sk->sk_user_data;
1958c2ecf20Sopenharmony_ci	if (!conn) {
1968c2ecf20Sopenharmony_ci		read_unlock_bh(&sk->sk_callback_lock);
1978c2ecf20Sopenharmony_ci		return;
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	tcp_conn = conn->dd_data;
2018c2ecf20Sopenharmony_ci	tcp_sw_conn = tcp_conn->dd_data;
2028c2ecf20Sopenharmony_ci	old_write_space = tcp_sw_conn->old_write_space;
2038c2ecf20Sopenharmony_ci	read_unlock_bh(&sk->sk_callback_lock);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	old_write_space(sk);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	ISCSI_SW_TCP_DBG(conn, "iscsi_write_space\n");
2088c2ecf20Sopenharmony_ci	iscsi_conn_queue_work(conn);
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic void iscsi_sw_tcp_conn_set_callbacks(struct iscsi_conn *conn)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
2148c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
2158c2ecf20Sopenharmony_ci	struct sock *sk = tcp_sw_conn->sock->sk;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	/* assign new callbacks */
2188c2ecf20Sopenharmony_ci	write_lock_bh(&sk->sk_callback_lock);
2198c2ecf20Sopenharmony_ci	sk->sk_user_data = conn;
2208c2ecf20Sopenharmony_ci	tcp_sw_conn->old_data_ready = sk->sk_data_ready;
2218c2ecf20Sopenharmony_ci	tcp_sw_conn->old_state_change = sk->sk_state_change;
2228c2ecf20Sopenharmony_ci	tcp_sw_conn->old_write_space = sk->sk_write_space;
2238c2ecf20Sopenharmony_ci	sk->sk_data_ready = iscsi_sw_tcp_data_ready;
2248c2ecf20Sopenharmony_ci	sk->sk_state_change = iscsi_sw_tcp_state_change;
2258c2ecf20Sopenharmony_ci	sk->sk_write_space = iscsi_sw_tcp_write_space;
2268c2ecf20Sopenharmony_ci	write_unlock_bh(&sk->sk_callback_lock);
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic void
2308c2ecf20Sopenharmony_ciiscsi_sw_tcp_conn_restore_callbacks(struct iscsi_conn *conn)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
2338c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
2348c2ecf20Sopenharmony_ci	struct sock *sk = tcp_sw_conn->sock->sk;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	/* restore socket callbacks, see also: iscsi_conn_set_callbacks() */
2378c2ecf20Sopenharmony_ci	write_lock_bh(&sk->sk_callback_lock);
2388c2ecf20Sopenharmony_ci	sk->sk_user_data    = NULL;
2398c2ecf20Sopenharmony_ci	sk->sk_data_ready   = tcp_sw_conn->old_data_ready;
2408c2ecf20Sopenharmony_ci	sk->sk_state_change = tcp_sw_conn->old_state_change;
2418c2ecf20Sopenharmony_ci	sk->sk_write_space  = tcp_sw_conn->old_write_space;
2428c2ecf20Sopenharmony_ci	sk->sk_no_check_tx = 0;
2438c2ecf20Sopenharmony_ci	write_unlock_bh(&sk->sk_callback_lock);
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci/**
2478c2ecf20Sopenharmony_ci * iscsi_sw_tcp_xmit_segment - transmit segment
2488c2ecf20Sopenharmony_ci * @tcp_conn: the iSCSI TCP connection
2498c2ecf20Sopenharmony_ci * @segment: the buffer to transmnit
2508c2ecf20Sopenharmony_ci *
2518c2ecf20Sopenharmony_ci * This function transmits as much of the buffer as
2528c2ecf20Sopenharmony_ci * the network layer will accept, and returns the number of
2538c2ecf20Sopenharmony_ci * bytes transmitted.
2548c2ecf20Sopenharmony_ci *
2558c2ecf20Sopenharmony_ci * If CRC hashing is enabled, the function will compute the
2568c2ecf20Sopenharmony_ci * hash as it goes. When the entire segment has been transmitted,
2578c2ecf20Sopenharmony_ci * it will retrieve the hash value and send it as well.
2588c2ecf20Sopenharmony_ci */
2598c2ecf20Sopenharmony_cistatic int iscsi_sw_tcp_xmit_segment(struct iscsi_tcp_conn *tcp_conn,
2608c2ecf20Sopenharmony_ci				     struct iscsi_segment *segment)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
2638c2ecf20Sopenharmony_ci	struct socket *sk = tcp_sw_conn->sock;
2648c2ecf20Sopenharmony_ci	unsigned int copied = 0;
2658c2ecf20Sopenharmony_ci	int r = 0;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	while (!iscsi_tcp_segment_done(tcp_conn, segment, 0, r)) {
2688c2ecf20Sopenharmony_ci		struct scatterlist *sg;
2698c2ecf20Sopenharmony_ci		unsigned int offset, copy;
2708c2ecf20Sopenharmony_ci		int flags = 0;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci		r = 0;
2738c2ecf20Sopenharmony_ci		offset = segment->copied;
2748c2ecf20Sopenharmony_ci		copy = segment->size - offset;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci		if (segment->total_copied + segment->size < segment->total_size)
2778c2ecf20Sopenharmony_ci			flags |= MSG_MORE;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci		/* Use sendpage if we can; else fall back to sendmsg */
2808c2ecf20Sopenharmony_ci		if (!segment->data) {
2818c2ecf20Sopenharmony_ci			sg = segment->sg;
2828c2ecf20Sopenharmony_ci			offset += segment->sg_offset + sg->offset;
2838c2ecf20Sopenharmony_ci			r = tcp_sw_conn->sendpage(sk, sg_page(sg), offset,
2848c2ecf20Sopenharmony_ci						  copy, flags);
2858c2ecf20Sopenharmony_ci		} else {
2868c2ecf20Sopenharmony_ci			struct msghdr msg = { .msg_flags = flags };
2878c2ecf20Sopenharmony_ci			struct kvec iov = {
2888c2ecf20Sopenharmony_ci				.iov_base = segment->data + offset,
2898c2ecf20Sopenharmony_ci				.iov_len = copy
2908c2ecf20Sopenharmony_ci			};
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci			r = kernel_sendmsg(sk, &msg, &iov, 1, copy);
2938c2ecf20Sopenharmony_ci		}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci		if (r < 0) {
2968c2ecf20Sopenharmony_ci			iscsi_tcp_segment_unmap(segment);
2978c2ecf20Sopenharmony_ci			return r;
2988c2ecf20Sopenharmony_ci		}
2998c2ecf20Sopenharmony_ci		copied += r;
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci	return copied;
3028c2ecf20Sopenharmony_ci}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci/**
3058c2ecf20Sopenharmony_ci * iscsi_sw_tcp_xmit - TCP transmit
3068c2ecf20Sopenharmony_ci * @conn: iscsi connection
3078c2ecf20Sopenharmony_ci **/
3088c2ecf20Sopenharmony_cistatic int iscsi_sw_tcp_xmit(struct iscsi_conn *conn)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
3118c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
3128c2ecf20Sopenharmony_ci	struct iscsi_segment *segment = &tcp_sw_conn->out.segment;
3138c2ecf20Sopenharmony_ci	unsigned int consumed = 0;
3148c2ecf20Sopenharmony_ci	int rc = 0;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	while (1) {
3178c2ecf20Sopenharmony_ci		rc = iscsi_sw_tcp_xmit_segment(tcp_conn, segment);
3188c2ecf20Sopenharmony_ci		/*
3198c2ecf20Sopenharmony_ci		 * We may not have been able to send data because the conn
3208c2ecf20Sopenharmony_ci		 * is getting stopped. libiscsi will know so propagate err
3218c2ecf20Sopenharmony_ci		 * for it to do the right thing.
3228c2ecf20Sopenharmony_ci		 */
3238c2ecf20Sopenharmony_ci		if (rc == -EAGAIN)
3248c2ecf20Sopenharmony_ci			return rc;
3258c2ecf20Sopenharmony_ci		else if (rc < 0) {
3268c2ecf20Sopenharmony_ci			rc = ISCSI_ERR_XMIT_FAILED;
3278c2ecf20Sopenharmony_ci			goto error;
3288c2ecf20Sopenharmony_ci		} else if (rc == 0)
3298c2ecf20Sopenharmony_ci			break;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci		consumed += rc;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci		if (segment->total_copied >= segment->total_size) {
3348c2ecf20Sopenharmony_ci			if (segment->done != NULL) {
3358c2ecf20Sopenharmony_ci				rc = segment->done(tcp_conn, segment);
3368c2ecf20Sopenharmony_ci				if (rc != 0)
3378c2ecf20Sopenharmony_ci					goto error;
3388c2ecf20Sopenharmony_ci			}
3398c2ecf20Sopenharmony_ci		}
3408c2ecf20Sopenharmony_ci	}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	ISCSI_SW_TCP_DBG(conn, "xmit %d bytes\n", consumed);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	conn->txdata_octets += consumed;
3458c2ecf20Sopenharmony_ci	return consumed;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cierror:
3488c2ecf20Sopenharmony_ci	/* Transmit error. We could initiate error recovery
3498c2ecf20Sopenharmony_ci	 * here. */
3508c2ecf20Sopenharmony_ci	ISCSI_SW_TCP_DBG(conn, "Error sending PDU, errno=%d\n", rc);
3518c2ecf20Sopenharmony_ci	iscsi_conn_failure(conn, rc);
3528c2ecf20Sopenharmony_ci	return -EIO;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci/**
3568c2ecf20Sopenharmony_ci * iscsi_tcp_xmit_qlen - return the number of bytes queued for xmit
3578c2ecf20Sopenharmony_ci * @conn: iscsi connection
3588c2ecf20Sopenharmony_ci */
3598c2ecf20Sopenharmony_cistatic inline int iscsi_sw_tcp_xmit_qlen(struct iscsi_conn *conn)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
3628c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
3638c2ecf20Sopenharmony_ci	struct iscsi_segment *segment = &tcp_sw_conn->out.segment;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	return segment->total_copied - segment->total_size;
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic int iscsi_sw_tcp_pdu_xmit(struct iscsi_task *task)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = task->conn;
3718c2ecf20Sopenharmony_ci	unsigned int noreclaim_flag;
3728c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
3738c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
3748c2ecf20Sopenharmony_ci	int rc = 0;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	if (!tcp_sw_conn->sock) {
3778c2ecf20Sopenharmony_ci		iscsi_conn_printk(KERN_ERR, conn,
3788c2ecf20Sopenharmony_ci				  "Transport not bound to socket!\n");
3798c2ecf20Sopenharmony_ci		return -EINVAL;
3808c2ecf20Sopenharmony_ci	}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	noreclaim_flag = memalloc_noreclaim_save();
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	while (iscsi_sw_tcp_xmit_qlen(conn)) {
3858c2ecf20Sopenharmony_ci		rc = iscsi_sw_tcp_xmit(conn);
3868c2ecf20Sopenharmony_ci		if (rc == 0) {
3878c2ecf20Sopenharmony_ci			rc = -EAGAIN;
3888c2ecf20Sopenharmony_ci			break;
3898c2ecf20Sopenharmony_ci		}
3908c2ecf20Sopenharmony_ci		if (rc < 0)
3918c2ecf20Sopenharmony_ci			break;
3928c2ecf20Sopenharmony_ci		rc = 0;
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	memalloc_noreclaim_restore(noreclaim_flag);
3968c2ecf20Sopenharmony_ci	return rc;
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci/*
4008c2ecf20Sopenharmony_ci * This is called when we're done sending the header.
4018c2ecf20Sopenharmony_ci * Simply copy the data_segment to the send segment, and return.
4028c2ecf20Sopenharmony_ci */
4038c2ecf20Sopenharmony_cistatic int iscsi_sw_tcp_send_hdr_done(struct iscsi_tcp_conn *tcp_conn,
4048c2ecf20Sopenharmony_ci				      struct iscsi_segment *segment)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	tcp_sw_conn->out.segment = tcp_sw_conn->out.data_segment;
4098c2ecf20Sopenharmony_ci	ISCSI_SW_TCP_DBG(tcp_conn->iscsi_conn,
4108c2ecf20Sopenharmony_ci			 "Header done. Next segment size %u total_size %u\n",
4118c2ecf20Sopenharmony_ci			 tcp_sw_conn->out.segment.size,
4128c2ecf20Sopenharmony_ci			 tcp_sw_conn->out.segment.total_size);
4138c2ecf20Sopenharmony_ci	return 0;
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_cistatic void iscsi_sw_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr,
4178c2ecf20Sopenharmony_ci				       size_t hdrlen)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
4208c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	ISCSI_SW_TCP_DBG(conn, "%s\n", conn->hdrdgst_en ?
4238c2ecf20Sopenharmony_ci			 "digest enabled" : "digest disabled");
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	/* Clear the data segment - needs to be filled in by the
4268c2ecf20Sopenharmony_ci	 * caller using iscsi_tcp_send_data_prep() */
4278c2ecf20Sopenharmony_ci	memset(&tcp_sw_conn->out.data_segment, 0,
4288c2ecf20Sopenharmony_ci	       sizeof(struct iscsi_segment));
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	/* If header digest is enabled, compute the CRC and
4318c2ecf20Sopenharmony_ci	 * place the digest into the same buffer. We make
4328c2ecf20Sopenharmony_ci	 * sure that both iscsi_tcp_task and mtask have
4338c2ecf20Sopenharmony_ci	 * sufficient room.
4348c2ecf20Sopenharmony_ci	 */
4358c2ecf20Sopenharmony_ci	if (conn->hdrdgst_en) {
4368c2ecf20Sopenharmony_ci		iscsi_tcp_dgst_header(tcp_sw_conn->tx_hash, hdr, hdrlen,
4378c2ecf20Sopenharmony_ci				      hdr + hdrlen);
4388c2ecf20Sopenharmony_ci		hdrlen += ISCSI_DIGEST_SIZE;
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	/* Remember header pointer for later, when we need
4428c2ecf20Sopenharmony_ci	 * to decide whether there's a payload to go along
4438c2ecf20Sopenharmony_ci	 * with the header. */
4448c2ecf20Sopenharmony_ci	tcp_sw_conn->out.hdr = hdr;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	iscsi_segment_init_linear(&tcp_sw_conn->out.segment, hdr, hdrlen,
4478c2ecf20Sopenharmony_ci				  iscsi_sw_tcp_send_hdr_done, NULL);
4488c2ecf20Sopenharmony_ci}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci/*
4518c2ecf20Sopenharmony_ci * Prepare the send buffer for the payload data.
4528c2ecf20Sopenharmony_ci * Padding and checksumming will all be taken care
4538c2ecf20Sopenharmony_ci * of by the iscsi_segment routines.
4548c2ecf20Sopenharmony_ci */
4558c2ecf20Sopenharmony_cistatic int
4568c2ecf20Sopenharmony_ciiscsi_sw_tcp_send_data_prep(struct iscsi_conn *conn, struct scatterlist *sg,
4578c2ecf20Sopenharmony_ci			    unsigned int count, unsigned int offset,
4588c2ecf20Sopenharmony_ci			    unsigned int len)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
4618c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
4628c2ecf20Sopenharmony_ci	struct ahash_request *tx_hash = NULL;
4638c2ecf20Sopenharmony_ci	unsigned int hdr_spec_len;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	ISCSI_SW_TCP_DBG(conn, "offset=%d, datalen=%d %s\n", offset, len,
4668c2ecf20Sopenharmony_ci			 conn->datadgst_en ?
4678c2ecf20Sopenharmony_ci			 "digest enabled" : "digest disabled");
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	/* Make sure the datalen matches what the caller
4708c2ecf20Sopenharmony_ci	   said he would send. */
4718c2ecf20Sopenharmony_ci	hdr_spec_len = ntoh24(tcp_sw_conn->out.hdr->dlength);
4728c2ecf20Sopenharmony_ci	WARN_ON(iscsi_padded(len) != iscsi_padded(hdr_spec_len));
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	if (conn->datadgst_en)
4758c2ecf20Sopenharmony_ci		tx_hash = tcp_sw_conn->tx_hash;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	return iscsi_segment_seek_sg(&tcp_sw_conn->out.data_segment,
4788c2ecf20Sopenharmony_ci				     sg, count, offset, len,
4798c2ecf20Sopenharmony_ci				     NULL, tx_hash);
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_cistatic void
4838c2ecf20Sopenharmony_ciiscsi_sw_tcp_send_linear_data_prep(struct iscsi_conn *conn, void *data,
4848c2ecf20Sopenharmony_ci				   size_t len)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
4878c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
4888c2ecf20Sopenharmony_ci	struct ahash_request *tx_hash = NULL;
4898c2ecf20Sopenharmony_ci	unsigned int hdr_spec_len;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	ISCSI_SW_TCP_DBG(conn, "datalen=%zd %s\n", len, conn->datadgst_en ?
4928c2ecf20Sopenharmony_ci			 "digest enabled" : "digest disabled");
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	/* Make sure the datalen matches what the caller
4958c2ecf20Sopenharmony_ci	   said he would send. */
4968c2ecf20Sopenharmony_ci	hdr_spec_len = ntoh24(tcp_sw_conn->out.hdr->dlength);
4978c2ecf20Sopenharmony_ci	WARN_ON(iscsi_padded(len) != iscsi_padded(hdr_spec_len));
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	if (conn->datadgst_en)
5008c2ecf20Sopenharmony_ci		tx_hash = tcp_sw_conn->tx_hash;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	iscsi_segment_init_linear(&tcp_sw_conn->out.data_segment,
5038c2ecf20Sopenharmony_ci				data, len, NULL, tx_hash);
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_cistatic int iscsi_sw_tcp_pdu_init(struct iscsi_task *task,
5078c2ecf20Sopenharmony_ci				 unsigned int offset, unsigned int count)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = task->conn;
5108c2ecf20Sopenharmony_ci	int err = 0;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	iscsi_sw_tcp_send_hdr_prep(conn, task->hdr, task->hdr_len);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	if (!count)
5158c2ecf20Sopenharmony_ci		return 0;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	if (!task->sc)
5188c2ecf20Sopenharmony_ci		iscsi_sw_tcp_send_linear_data_prep(conn, task->data, count);
5198c2ecf20Sopenharmony_ci	else {
5208c2ecf20Sopenharmony_ci		struct scsi_data_buffer *sdb = &task->sc->sdb;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci		err = iscsi_sw_tcp_send_data_prep(conn, sdb->table.sgl,
5238c2ecf20Sopenharmony_ci						  sdb->table.nents, offset,
5248c2ecf20Sopenharmony_ci						  count);
5258c2ecf20Sopenharmony_ci	}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	if (err) {
5288c2ecf20Sopenharmony_ci		/* got invalid offset/len */
5298c2ecf20Sopenharmony_ci		return -EIO;
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci	return 0;
5328c2ecf20Sopenharmony_ci}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cistatic int iscsi_sw_tcp_pdu_alloc(struct iscsi_task *task, uint8_t opcode)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	struct iscsi_tcp_task *tcp_task = task->dd_data;
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	task->hdr = task->dd_data + sizeof(*tcp_task);
5398c2ecf20Sopenharmony_ci	task->hdr_max = sizeof(struct iscsi_sw_tcp_hdrbuf) - ISCSI_DIGEST_SIZE;
5408c2ecf20Sopenharmony_ci	return 0;
5418c2ecf20Sopenharmony_ci}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_cistatic struct iscsi_cls_conn *
5448c2ecf20Sopenharmony_ciiscsi_sw_tcp_conn_create(struct iscsi_cls_session *cls_session,
5458c2ecf20Sopenharmony_ci			 uint32_t conn_idx)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	struct iscsi_conn *conn;
5488c2ecf20Sopenharmony_ci	struct iscsi_cls_conn *cls_conn;
5498c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn;
5508c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_conn *tcp_sw_conn;
5518c2ecf20Sopenharmony_ci	struct crypto_ahash *tfm;
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	cls_conn = iscsi_tcp_conn_setup(cls_session, sizeof(*tcp_sw_conn),
5548c2ecf20Sopenharmony_ci					conn_idx);
5558c2ecf20Sopenharmony_ci	if (!cls_conn)
5568c2ecf20Sopenharmony_ci		return NULL;
5578c2ecf20Sopenharmony_ci	conn = cls_conn->dd_data;
5588c2ecf20Sopenharmony_ci	tcp_conn = conn->dd_data;
5598c2ecf20Sopenharmony_ci	tcp_sw_conn = tcp_conn->dd_data;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	mutex_init(&tcp_sw_conn->sock_lock);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	tfm = crypto_alloc_ahash("crc32c", 0, CRYPTO_ALG_ASYNC);
5648c2ecf20Sopenharmony_ci	if (IS_ERR(tfm))
5658c2ecf20Sopenharmony_ci		goto free_conn;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	tcp_sw_conn->tx_hash = ahash_request_alloc(tfm, GFP_KERNEL);
5688c2ecf20Sopenharmony_ci	if (!tcp_sw_conn->tx_hash)
5698c2ecf20Sopenharmony_ci		goto free_tfm;
5708c2ecf20Sopenharmony_ci	ahash_request_set_callback(tcp_sw_conn->tx_hash, 0, NULL, NULL);
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	tcp_sw_conn->rx_hash = ahash_request_alloc(tfm, GFP_KERNEL);
5738c2ecf20Sopenharmony_ci	if (!tcp_sw_conn->rx_hash)
5748c2ecf20Sopenharmony_ci		goto free_tx_hash;
5758c2ecf20Sopenharmony_ci	ahash_request_set_callback(tcp_sw_conn->rx_hash, 0, NULL, NULL);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	tcp_conn->rx_hash = tcp_sw_conn->rx_hash;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	return cls_conn;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_cifree_tx_hash:
5828c2ecf20Sopenharmony_ci	ahash_request_free(tcp_sw_conn->tx_hash);
5838c2ecf20Sopenharmony_cifree_tfm:
5848c2ecf20Sopenharmony_ci	crypto_free_ahash(tfm);
5858c2ecf20Sopenharmony_cifree_conn:
5868c2ecf20Sopenharmony_ci	iscsi_conn_printk(KERN_ERR, conn,
5878c2ecf20Sopenharmony_ci			  "Could not create connection due to crc32c "
5888c2ecf20Sopenharmony_ci			  "loading error. Make sure the crc32c "
5898c2ecf20Sopenharmony_ci			  "module is built as a module or into the "
5908c2ecf20Sopenharmony_ci			  "kernel\n");
5918c2ecf20Sopenharmony_ci	iscsi_tcp_conn_teardown(cls_conn);
5928c2ecf20Sopenharmony_ci	return NULL;
5938c2ecf20Sopenharmony_ci}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_cistatic void iscsi_sw_tcp_release_conn(struct iscsi_conn *conn)
5968c2ecf20Sopenharmony_ci{
5978c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
5988c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
5998c2ecf20Sopenharmony_ci	struct socket *sock = tcp_sw_conn->sock;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	/*
6028c2ecf20Sopenharmony_ci	 * The iscsi transport class will make sure we are not called in
6038c2ecf20Sopenharmony_ci	 * parallel with start, stop, bind and destroys. However, this can be
6048c2ecf20Sopenharmony_ci	 * called twice if userspace does a stop then a destroy.
6058c2ecf20Sopenharmony_ci	 */
6068c2ecf20Sopenharmony_ci	if (!sock)
6078c2ecf20Sopenharmony_ci		return;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	sock_hold(sock->sk);
6108c2ecf20Sopenharmony_ci	iscsi_sw_tcp_conn_restore_callbacks(conn);
6118c2ecf20Sopenharmony_ci	sock_put(sock->sk);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	mutex_lock(&tcp_sw_conn->sock_lock);
6148c2ecf20Sopenharmony_ci	tcp_sw_conn->sock = NULL;
6158c2ecf20Sopenharmony_ci	mutex_unlock(&tcp_sw_conn->sock_lock);
6168c2ecf20Sopenharmony_ci	sockfd_put(sock);
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistatic void iscsi_sw_tcp_conn_destroy(struct iscsi_cls_conn *cls_conn)
6208c2ecf20Sopenharmony_ci{
6218c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cls_conn->dd_data;
6228c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
6238c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	iscsi_sw_tcp_release_conn(conn);
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	ahash_request_free(tcp_sw_conn->rx_hash);
6288c2ecf20Sopenharmony_ci	if (tcp_sw_conn->tx_hash) {
6298c2ecf20Sopenharmony_ci		struct crypto_ahash *tfm;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci		tfm = crypto_ahash_reqtfm(tcp_sw_conn->tx_hash);
6328c2ecf20Sopenharmony_ci		ahash_request_free(tcp_sw_conn->tx_hash);
6338c2ecf20Sopenharmony_ci		crypto_free_ahash(tfm);
6348c2ecf20Sopenharmony_ci	}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	iscsi_tcp_conn_teardown(cls_conn);
6378c2ecf20Sopenharmony_ci}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_cistatic void iscsi_sw_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
6408c2ecf20Sopenharmony_ci{
6418c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cls_conn->dd_data;
6428c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
6438c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
6448c2ecf20Sopenharmony_ci	struct socket *sock = tcp_sw_conn->sock;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	/* userspace may have goofed up and not bound us */
6478c2ecf20Sopenharmony_ci	if (!sock)
6488c2ecf20Sopenharmony_ci		return;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	sock->sk->sk_err = EIO;
6518c2ecf20Sopenharmony_ci	wake_up_interruptible(sk_sleep(sock->sk));
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	/* stop xmit side */
6548c2ecf20Sopenharmony_ci	iscsi_suspend_tx(conn);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	/* stop recv side and release socket */
6578c2ecf20Sopenharmony_ci	iscsi_sw_tcp_release_conn(conn);
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	iscsi_conn_stop(cls_conn, flag);
6608c2ecf20Sopenharmony_ci}
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_cistatic int
6638c2ecf20Sopenharmony_ciiscsi_sw_tcp_conn_bind(struct iscsi_cls_session *cls_session,
6648c2ecf20Sopenharmony_ci		       struct iscsi_cls_conn *cls_conn, uint64_t transport_eph,
6658c2ecf20Sopenharmony_ci		       int is_leading)
6668c2ecf20Sopenharmony_ci{
6678c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cls_conn->dd_data;
6688c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
6698c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
6708c2ecf20Sopenharmony_ci	struct sock *sk;
6718c2ecf20Sopenharmony_ci	struct socket *sock;
6728c2ecf20Sopenharmony_ci	int err;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	/* lookup for existing socket */
6758c2ecf20Sopenharmony_ci	sock = sockfd_lookup((int)transport_eph, &err);
6768c2ecf20Sopenharmony_ci	if (!sock) {
6778c2ecf20Sopenharmony_ci		iscsi_conn_printk(KERN_ERR, conn,
6788c2ecf20Sopenharmony_ci				  "sockfd_lookup failed %d\n", err);
6798c2ecf20Sopenharmony_ci		return -EEXIST;
6808c2ecf20Sopenharmony_ci	}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	err = iscsi_conn_bind(cls_session, cls_conn, is_leading);
6838c2ecf20Sopenharmony_ci	if (err)
6848c2ecf20Sopenharmony_ci		goto free_socket;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	mutex_lock(&tcp_sw_conn->sock_lock);
6878c2ecf20Sopenharmony_ci	/* bind iSCSI connection and socket */
6888c2ecf20Sopenharmony_ci	tcp_sw_conn->sock = sock;
6898c2ecf20Sopenharmony_ci	mutex_unlock(&tcp_sw_conn->sock_lock);
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	/* setup Socket parameters */
6928c2ecf20Sopenharmony_ci	sk = sock->sk;
6938c2ecf20Sopenharmony_ci	sk->sk_reuse = SK_CAN_REUSE;
6948c2ecf20Sopenharmony_ci	sk->sk_sndtimeo = 15 * HZ; /* FIXME: make it configurable */
6958c2ecf20Sopenharmony_ci	sk->sk_allocation = GFP_ATOMIC;
6968c2ecf20Sopenharmony_ci	sk_set_memalloc(sk);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	iscsi_sw_tcp_conn_set_callbacks(conn);
6998c2ecf20Sopenharmony_ci	tcp_sw_conn->sendpage = tcp_sw_conn->sock->ops->sendpage;
7008c2ecf20Sopenharmony_ci	/*
7018c2ecf20Sopenharmony_ci	 * set receive state machine into initial state
7028c2ecf20Sopenharmony_ci	 */
7038c2ecf20Sopenharmony_ci	iscsi_tcp_hdr_recv_prep(tcp_conn);
7048c2ecf20Sopenharmony_ci	return 0;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_cifree_socket:
7078c2ecf20Sopenharmony_ci	sockfd_put(sock);
7088c2ecf20Sopenharmony_ci	return err;
7098c2ecf20Sopenharmony_ci}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_cistatic int iscsi_sw_tcp_conn_set_param(struct iscsi_cls_conn *cls_conn,
7128c2ecf20Sopenharmony_ci				       enum iscsi_param param, char *buf,
7138c2ecf20Sopenharmony_ci				       int buflen)
7148c2ecf20Sopenharmony_ci{
7158c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cls_conn->dd_data;
7168c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
7178c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	switch(param) {
7208c2ecf20Sopenharmony_ci	case ISCSI_PARAM_HDRDGST_EN:
7218c2ecf20Sopenharmony_ci		iscsi_set_param(cls_conn, param, buf, buflen);
7228c2ecf20Sopenharmony_ci		break;
7238c2ecf20Sopenharmony_ci	case ISCSI_PARAM_DATADGST_EN:
7248c2ecf20Sopenharmony_ci		mutex_lock(&tcp_sw_conn->sock_lock);
7258c2ecf20Sopenharmony_ci		if (!tcp_sw_conn->sock) {
7268c2ecf20Sopenharmony_ci			mutex_unlock(&tcp_sw_conn->sock_lock);
7278c2ecf20Sopenharmony_ci			return -ENOTCONN;
7288c2ecf20Sopenharmony_ci		}
7298c2ecf20Sopenharmony_ci		iscsi_set_param(cls_conn, param, buf, buflen);
7308c2ecf20Sopenharmony_ci		tcp_sw_conn->sendpage = conn->datadgst_en ?
7318c2ecf20Sopenharmony_ci			sock_no_sendpage : tcp_sw_conn->sock->ops->sendpage;
7328c2ecf20Sopenharmony_ci		mutex_unlock(&tcp_sw_conn->sock_lock);
7338c2ecf20Sopenharmony_ci		break;
7348c2ecf20Sopenharmony_ci	case ISCSI_PARAM_MAX_R2T:
7358c2ecf20Sopenharmony_ci		return iscsi_tcp_set_max_r2t(conn, buf);
7368c2ecf20Sopenharmony_ci	default:
7378c2ecf20Sopenharmony_ci		return iscsi_set_param(cls_conn, param, buf, buflen);
7388c2ecf20Sopenharmony_ci	}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	return 0;
7418c2ecf20Sopenharmony_ci}
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_cistatic int iscsi_sw_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn,
7448c2ecf20Sopenharmony_ci				       enum iscsi_param param, char *buf)
7458c2ecf20Sopenharmony_ci{
7468c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cls_conn->dd_data;
7478c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_conn *tcp_sw_conn;
7488c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn;
7498c2ecf20Sopenharmony_ci	struct sockaddr_in6 addr;
7508c2ecf20Sopenharmony_ci	struct socket *sock;
7518c2ecf20Sopenharmony_ci	int rc;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	switch(param) {
7548c2ecf20Sopenharmony_ci	case ISCSI_PARAM_CONN_PORT:
7558c2ecf20Sopenharmony_ci	case ISCSI_PARAM_CONN_ADDRESS:
7568c2ecf20Sopenharmony_ci	case ISCSI_PARAM_LOCAL_PORT:
7578c2ecf20Sopenharmony_ci		spin_lock_bh(&conn->session->frwd_lock);
7588c2ecf20Sopenharmony_ci		if (!conn->session->leadconn) {
7598c2ecf20Sopenharmony_ci			spin_unlock_bh(&conn->session->frwd_lock);
7608c2ecf20Sopenharmony_ci			return -ENOTCONN;
7618c2ecf20Sopenharmony_ci		}
7628c2ecf20Sopenharmony_ci		/*
7638c2ecf20Sopenharmony_ci		 * The conn has been setup and bound, so just grab a ref
7648c2ecf20Sopenharmony_ci		 * incase a destroy runs while we are in the net layer.
7658c2ecf20Sopenharmony_ci		 */
7668c2ecf20Sopenharmony_ci		iscsi_get_conn(conn->cls_conn);
7678c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->session->frwd_lock);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci		tcp_conn = conn->dd_data;
7708c2ecf20Sopenharmony_ci		tcp_sw_conn = tcp_conn->dd_data;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci		mutex_lock(&tcp_sw_conn->sock_lock);
7738c2ecf20Sopenharmony_ci		sock = tcp_sw_conn->sock;
7748c2ecf20Sopenharmony_ci		if (!sock) {
7758c2ecf20Sopenharmony_ci			rc = -ENOTCONN;
7768c2ecf20Sopenharmony_ci			goto sock_unlock;
7778c2ecf20Sopenharmony_ci		}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci		if (param == ISCSI_PARAM_LOCAL_PORT)
7808c2ecf20Sopenharmony_ci			rc = kernel_getsockname(sock,
7818c2ecf20Sopenharmony_ci						(struct sockaddr *)&addr);
7828c2ecf20Sopenharmony_ci		else
7838c2ecf20Sopenharmony_ci			rc = kernel_getpeername(sock,
7848c2ecf20Sopenharmony_ci						(struct sockaddr *)&addr);
7858c2ecf20Sopenharmony_cisock_unlock:
7868c2ecf20Sopenharmony_ci		mutex_unlock(&tcp_sw_conn->sock_lock);
7878c2ecf20Sopenharmony_ci		iscsi_put_conn(conn->cls_conn);
7888c2ecf20Sopenharmony_ci		if (rc < 0)
7898c2ecf20Sopenharmony_ci			return rc;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci		return iscsi_conn_get_addr_param((struct sockaddr_storage *)
7928c2ecf20Sopenharmony_ci						 &addr, param, buf);
7938c2ecf20Sopenharmony_ci	default:
7948c2ecf20Sopenharmony_ci		return iscsi_conn_get_param(cls_conn, param, buf);
7958c2ecf20Sopenharmony_ci	}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	return 0;
7988c2ecf20Sopenharmony_ci}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_cistatic int iscsi_sw_tcp_host_get_param(struct Scsi_Host *shost,
8018c2ecf20Sopenharmony_ci				       enum iscsi_host_param param, char *buf)
8028c2ecf20Sopenharmony_ci{
8038c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_host *tcp_sw_host = iscsi_host_priv(shost);
8048c2ecf20Sopenharmony_ci	struct iscsi_session *session;
8058c2ecf20Sopenharmony_ci	struct iscsi_conn *conn;
8068c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn;
8078c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_conn *tcp_sw_conn;
8088c2ecf20Sopenharmony_ci	struct sockaddr_in6 addr;
8098c2ecf20Sopenharmony_ci	struct socket *sock;
8108c2ecf20Sopenharmony_ci	int rc;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	switch (param) {
8138c2ecf20Sopenharmony_ci	case ISCSI_HOST_PARAM_IPADDRESS:
8148c2ecf20Sopenharmony_ci		session = tcp_sw_host->session;
8158c2ecf20Sopenharmony_ci		if (!session)
8168c2ecf20Sopenharmony_ci			return -ENOTCONN;
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci		spin_lock_bh(&session->frwd_lock);
8198c2ecf20Sopenharmony_ci		conn = session->leadconn;
8208c2ecf20Sopenharmony_ci		if (!conn) {
8218c2ecf20Sopenharmony_ci			spin_unlock_bh(&session->frwd_lock);
8228c2ecf20Sopenharmony_ci			return -ENOTCONN;
8238c2ecf20Sopenharmony_ci		}
8248c2ecf20Sopenharmony_ci		tcp_conn = conn->dd_data;
8258c2ecf20Sopenharmony_ci		tcp_sw_conn = tcp_conn->dd_data;
8268c2ecf20Sopenharmony_ci		/*
8278c2ecf20Sopenharmony_ci		 * The conn has been setup and bound, so just grab a ref
8288c2ecf20Sopenharmony_ci		 * incase a destroy runs while we are in the net layer.
8298c2ecf20Sopenharmony_ci		 */
8308c2ecf20Sopenharmony_ci		iscsi_get_conn(conn->cls_conn);
8318c2ecf20Sopenharmony_ci		spin_unlock_bh(&session->frwd_lock);
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci		mutex_lock(&tcp_sw_conn->sock_lock);
8348c2ecf20Sopenharmony_ci		sock = tcp_sw_conn->sock;
8358c2ecf20Sopenharmony_ci		if (!sock)
8368c2ecf20Sopenharmony_ci			rc = -ENOTCONN;
8378c2ecf20Sopenharmony_ci		else
8388c2ecf20Sopenharmony_ci			rc = kernel_getsockname(sock, (struct sockaddr *)&addr);
8398c2ecf20Sopenharmony_ci		mutex_unlock(&tcp_sw_conn->sock_lock);
8408c2ecf20Sopenharmony_ci		iscsi_put_conn(conn->cls_conn);
8418c2ecf20Sopenharmony_ci		if (rc < 0)
8428c2ecf20Sopenharmony_ci			return rc;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci		return iscsi_conn_get_addr_param((struct sockaddr_storage *)
8458c2ecf20Sopenharmony_ci						 &addr,
8468c2ecf20Sopenharmony_ci						 (enum iscsi_param)param, buf);
8478c2ecf20Sopenharmony_ci	default:
8488c2ecf20Sopenharmony_ci		return iscsi_host_get_param(shost, param, buf);
8498c2ecf20Sopenharmony_ci	}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	return 0;
8528c2ecf20Sopenharmony_ci}
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_cistatic void
8558c2ecf20Sopenharmony_ciiscsi_sw_tcp_conn_get_stats(struct iscsi_cls_conn *cls_conn,
8568c2ecf20Sopenharmony_ci			    struct iscsi_stats *stats)
8578c2ecf20Sopenharmony_ci{
8588c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cls_conn->dd_data;
8598c2ecf20Sopenharmony_ci	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
8608c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	stats->custom_length = 3;
8638c2ecf20Sopenharmony_ci	strcpy(stats->custom[0].desc, "tx_sendpage_failures");
8648c2ecf20Sopenharmony_ci	stats->custom[0].value = tcp_sw_conn->sendpage_failures_cnt;
8658c2ecf20Sopenharmony_ci	strcpy(stats->custom[1].desc, "rx_discontiguous_hdr");
8668c2ecf20Sopenharmony_ci	stats->custom[1].value = tcp_sw_conn->discontiguous_hdr_cnt;
8678c2ecf20Sopenharmony_ci	strcpy(stats->custom[2].desc, "eh_abort_cnt");
8688c2ecf20Sopenharmony_ci	stats->custom[2].value = conn->eh_abort_cnt;
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	iscsi_tcp_conn_get_stats(cls_conn, stats);
8718c2ecf20Sopenharmony_ci}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_cistatic struct iscsi_cls_session *
8748c2ecf20Sopenharmony_ciiscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
8758c2ecf20Sopenharmony_ci			    uint16_t qdepth, uint32_t initial_cmdsn)
8768c2ecf20Sopenharmony_ci{
8778c2ecf20Sopenharmony_ci	struct iscsi_cls_session *cls_session;
8788c2ecf20Sopenharmony_ci	struct iscsi_session *session;
8798c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_host *tcp_sw_host;
8808c2ecf20Sopenharmony_ci	struct Scsi_Host *shost;
8818c2ecf20Sopenharmony_ci	int rc;
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	if (ep) {
8848c2ecf20Sopenharmony_ci		printk(KERN_ERR "iscsi_tcp: invalid ep %p.\n", ep);
8858c2ecf20Sopenharmony_ci		return NULL;
8868c2ecf20Sopenharmony_ci	}
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	shost = iscsi_host_alloc(&iscsi_sw_tcp_sht,
8898c2ecf20Sopenharmony_ci				 sizeof(struct iscsi_sw_tcp_host), 1);
8908c2ecf20Sopenharmony_ci	if (!shost)
8918c2ecf20Sopenharmony_ci		return NULL;
8928c2ecf20Sopenharmony_ci	shost->transportt = iscsi_sw_tcp_scsi_transport;
8938c2ecf20Sopenharmony_ci	shost->cmd_per_lun = qdepth;
8948c2ecf20Sopenharmony_ci	shost->max_lun = iscsi_max_lun;
8958c2ecf20Sopenharmony_ci	shost->max_id = 0;
8968c2ecf20Sopenharmony_ci	shost->max_channel = 0;
8978c2ecf20Sopenharmony_ci	shost->max_cmd_len = SCSI_MAX_VARLEN_CDB_SIZE;
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	rc = iscsi_host_get_max_scsi_cmds(shost, cmds_max);
9008c2ecf20Sopenharmony_ci	if (rc < 0)
9018c2ecf20Sopenharmony_ci		goto free_host;
9028c2ecf20Sopenharmony_ci	shost->can_queue = rc;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	if (iscsi_host_add(shost, NULL))
9058c2ecf20Sopenharmony_ci		goto free_host;
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	cls_session = iscsi_session_setup(&iscsi_sw_tcp_transport, shost,
9088c2ecf20Sopenharmony_ci					  cmds_max, 0,
9098c2ecf20Sopenharmony_ci					  sizeof(struct iscsi_tcp_task) +
9108c2ecf20Sopenharmony_ci					  sizeof(struct iscsi_sw_tcp_hdrbuf),
9118c2ecf20Sopenharmony_ci					  initial_cmdsn, 0);
9128c2ecf20Sopenharmony_ci	if (!cls_session)
9138c2ecf20Sopenharmony_ci		goto remove_host;
9148c2ecf20Sopenharmony_ci	session = cls_session->dd_data;
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	if (iscsi_tcp_r2tpool_alloc(session))
9178c2ecf20Sopenharmony_ci		goto remove_session;
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	/* We are now fully setup so expose the session to sysfs. */
9208c2ecf20Sopenharmony_ci	tcp_sw_host = iscsi_host_priv(shost);
9218c2ecf20Sopenharmony_ci	tcp_sw_host->session = session;
9228c2ecf20Sopenharmony_ci	return cls_session;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ciremove_session:
9258c2ecf20Sopenharmony_ci	iscsi_session_teardown(cls_session);
9268c2ecf20Sopenharmony_ciremove_host:
9278c2ecf20Sopenharmony_ci	iscsi_host_remove(shost);
9288c2ecf20Sopenharmony_cifree_host:
9298c2ecf20Sopenharmony_ci	iscsi_host_free(shost);
9308c2ecf20Sopenharmony_ci	return NULL;
9318c2ecf20Sopenharmony_ci}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_cistatic void iscsi_sw_tcp_session_destroy(struct iscsi_cls_session *cls_session)
9348c2ecf20Sopenharmony_ci{
9358c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
9368c2ecf20Sopenharmony_ci	struct iscsi_session *session = cls_session->dd_data;
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(session->leadconn))
9398c2ecf20Sopenharmony_ci		return;
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	iscsi_tcp_r2tpool_free(cls_session->dd_data);
9428c2ecf20Sopenharmony_ci	iscsi_session_teardown(cls_session);
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	iscsi_host_remove(shost);
9458c2ecf20Sopenharmony_ci	iscsi_host_free(shost);
9468c2ecf20Sopenharmony_ci}
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_cistatic umode_t iscsi_sw_tcp_attr_is_visible(int param_type, int param)
9498c2ecf20Sopenharmony_ci{
9508c2ecf20Sopenharmony_ci	switch (param_type) {
9518c2ecf20Sopenharmony_ci	case ISCSI_HOST_PARAM:
9528c2ecf20Sopenharmony_ci		switch (param) {
9538c2ecf20Sopenharmony_ci		case ISCSI_HOST_PARAM_NETDEV_NAME:
9548c2ecf20Sopenharmony_ci		case ISCSI_HOST_PARAM_HWADDRESS:
9558c2ecf20Sopenharmony_ci		case ISCSI_HOST_PARAM_IPADDRESS:
9568c2ecf20Sopenharmony_ci		case ISCSI_HOST_PARAM_INITIATOR_NAME:
9578c2ecf20Sopenharmony_ci			return S_IRUGO;
9588c2ecf20Sopenharmony_ci		default:
9598c2ecf20Sopenharmony_ci			return 0;
9608c2ecf20Sopenharmony_ci		}
9618c2ecf20Sopenharmony_ci	case ISCSI_PARAM:
9628c2ecf20Sopenharmony_ci		switch (param) {
9638c2ecf20Sopenharmony_ci		case ISCSI_PARAM_MAX_RECV_DLENGTH:
9648c2ecf20Sopenharmony_ci		case ISCSI_PARAM_MAX_XMIT_DLENGTH:
9658c2ecf20Sopenharmony_ci		case ISCSI_PARAM_HDRDGST_EN:
9668c2ecf20Sopenharmony_ci		case ISCSI_PARAM_DATADGST_EN:
9678c2ecf20Sopenharmony_ci		case ISCSI_PARAM_CONN_ADDRESS:
9688c2ecf20Sopenharmony_ci		case ISCSI_PARAM_CONN_PORT:
9698c2ecf20Sopenharmony_ci		case ISCSI_PARAM_LOCAL_PORT:
9708c2ecf20Sopenharmony_ci		case ISCSI_PARAM_EXP_STATSN:
9718c2ecf20Sopenharmony_ci		case ISCSI_PARAM_PERSISTENT_ADDRESS:
9728c2ecf20Sopenharmony_ci		case ISCSI_PARAM_PERSISTENT_PORT:
9738c2ecf20Sopenharmony_ci		case ISCSI_PARAM_PING_TMO:
9748c2ecf20Sopenharmony_ci		case ISCSI_PARAM_RECV_TMO:
9758c2ecf20Sopenharmony_ci		case ISCSI_PARAM_INITIAL_R2T_EN:
9768c2ecf20Sopenharmony_ci		case ISCSI_PARAM_MAX_R2T:
9778c2ecf20Sopenharmony_ci		case ISCSI_PARAM_IMM_DATA_EN:
9788c2ecf20Sopenharmony_ci		case ISCSI_PARAM_FIRST_BURST:
9798c2ecf20Sopenharmony_ci		case ISCSI_PARAM_MAX_BURST:
9808c2ecf20Sopenharmony_ci		case ISCSI_PARAM_PDU_INORDER_EN:
9818c2ecf20Sopenharmony_ci		case ISCSI_PARAM_DATASEQ_INORDER_EN:
9828c2ecf20Sopenharmony_ci		case ISCSI_PARAM_ERL:
9838c2ecf20Sopenharmony_ci		case ISCSI_PARAM_TARGET_NAME:
9848c2ecf20Sopenharmony_ci		case ISCSI_PARAM_TPGT:
9858c2ecf20Sopenharmony_ci		case ISCSI_PARAM_USERNAME:
9868c2ecf20Sopenharmony_ci		case ISCSI_PARAM_PASSWORD:
9878c2ecf20Sopenharmony_ci		case ISCSI_PARAM_USERNAME_IN:
9888c2ecf20Sopenharmony_ci		case ISCSI_PARAM_PASSWORD_IN:
9898c2ecf20Sopenharmony_ci		case ISCSI_PARAM_FAST_ABORT:
9908c2ecf20Sopenharmony_ci		case ISCSI_PARAM_ABORT_TMO:
9918c2ecf20Sopenharmony_ci		case ISCSI_PARAM_LU_RESET_TMO:
9928c2ecf20Sopenharmony_ci		case ISCSI_PARAM_TGT_RESET_TMO:
9938c2ecf20Sopenharmony_ci		case ISCSI_PARAM_IFACE_NAME:
9948c2ecf20Sopenharmony_ci		case ISCSI_PARAM_INITIATOR_NAME:
9958c2ecf20Sopenharmony_ci			return S_IRUGO;
9968c2ecf20Sopenharmony_ci		default:
9978c2ecf20Sopenharmony_ci			return 0;
9988c2ecf20Sopenharmony_ci		}
9998c2ecf20Sopenharmony_ci	}
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	return 0;
10028c2ecf20Sopenharmony_ci}
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_cistatic int iscsi_sw_tcp_slave_configure(struct scsi_device *sdev)
10058c2ecf20Sopenharmony_ci{
10068c2ecf20Sopenharmony_ci	struct iscsi_sw_tcp_host *tcp_sw_host = iscsi_host_priv(sdev->host);
10078c2ecf20Sopenharmony_ci	struct iscsi_session *session = tcp_sw_host->session;
10088c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = session->leadconn;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	if (conn->datadgst_en)
10118c2ecf20Sopenharmony_ci		blk_queue_flag_set(QUEUE_FLAG_STABLE_WRITES,
10128c2ecf20Sopenharmony_ci				   sdev->request_queue);
10138c2ecf20Sopenharmony_ci	blk_queue_dma_alignment(sdev->request_queue, 0);
10148c2ecf20Sopenharmony_ci	return 0;
10158c2ecf20Sopenharmony_ci}
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_cistatic struct scsi_host_template iscsi_sw_tcp_sht = {
10188c2ecf20Sopenharmony_ci	.module			= THIS_MODULE,
10198c2ecf20Sopenharmony_ci	.name			= "iSCSI Initiator over TCP/IP",
10208c2ecf20Sopenharmony_ci	.queuecommand           = iscsi_queuecommand,
10218c2ecf20Sopenharmony_ci	.change_queue_depth	= scsi_change_queue_depth,
10228c2ecf20Sopenharmony_ci	.can_queue		= ISCSI_TOTAL_CMDS_MAX,
10238c2ecf20Sopenharmony_ci	.sg_tablesize		= 4096,
10248c2ecf20Sopenharmony_ci	.max_sectors		= 0xFFFF,
10258c2ecf20Sopenharmony_ci	.cmd_per_lun		= ISCSI_DEF_CMD_PER_LUN,
10268c2ecf20Sopenharmony_ci	.eh_timed_out		= iscsi_eh_cmd_timed_out,
10278c2ecf20Sopenharmony_ci	.eh_abort_handler       = iscsi_eh_abort,
10288c2ecf20Sopenharmony_ci	.eh_device_reset_handler= iscsi_eh_device_reset,
10298c2ecf20Sopenharmony_ci	.eh_target_reset_handler = iscsi_eh_recover_target,
10308c2ecf20Sopenharmony_ci	.dma_boundary		= PAGE_SIZE - 1,
10318c2ecf20Sopenharmony_ci	.slave_configure        = iscsi_sw_tcp_slave_configure,
10328c2ecf20Sopenharmony_ci	.target_alloc		= iscsi_target_alloc,
10338c2ecf20Sopenharmony_ci	.proc_name		= "iscsi_tcp",
10348c2ecf20Sopenharmony_ci	.this_id		= -1,
10358c2ecf20Sopenharmony_ci	.track_queue_depth	= 1,
10368c2ecf20Sopenharmony_ci};
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_cistatic struct iscsi_transport iscsi_sw_tcp_transport = {
10398c2ecf20Sopenharmony_ci	.owner			= THIS_MODULE,
10408c2ecf20Sopenharmony_ci	.name			= "tcp",
10418c2ecf20Sopenharmony_ci	.caps			= CAP_RECOVERY_L0 | CAP_MULTI_R2T | CAP_HDRDGST
10428c2ecf20Sopenharmony_ci				  | CAP_DATADGST,
10438c2ecf20Sopenharmony_ci	/* session management */
10448c2ecf20Sopenharmony_ci	.create_session		= iscsi_sw_tcp_session_create,
10458c2ecf20Sopenharmony_ci	.destroy_session	= iscsi_sw_tcp_session_destroy,
10468c2ecf20Sopenharmony_ci	/* connection management */
10478c2ecf20Sopenharmony_ci	.create_conn		= iscsi_sw_tcp_conn_create,
10488c2ecf20Sopenharmony_ci	.bind_conn		= iscsi_sw_tcp_conn_bind,
10498c2ecf20Sopenharmony_ci	.destroy_conn		= iscsi_sw_tcp_conn_destroy,
10508c2ecf20Sopenharmony_ci	.attr_is_visible	= iscsi_sw_tcp_attr_is_visible,
10518c2ecf20Sopenharmony_ci	.set_param		= iscsi_sw_tcp_conn_set_param,
10528c2ecf20Sopenharmony_ci	.get_conn_param		= iscsi_sw_tcp_conn_get_param,
10538c2ecf20Sopenharmony_ci	.get_session_param	= iscsi_session_get_param,
10548c2ecf20Sopenharmony_ci	.start_conn		= iscsi_conn_start,
10558c2ecf20Sopenharmony_ci	.stop_conn		= iscsi_sw_tcp_conn_stop,
10568c2ecf20Sopenharmony_ci	/* iscsi host params */
10578c2ecf20Sopenharmony_ci	.get_host_param		= iscsi_sw_tcp_host_get_param,
10588c2ecf20Sopenharmony_ci	.set_host_param		= iscsi_host_set_param,
10598c2ecf20Sopenharmony_ci	/* IO */
10608c2ecf20Sopenharmony_ci	.send_pdu		= iscsi_conn_send_pdu,
10618c2ecf20Sopenharmony_ci	.get_stats		= iscsi_sw_tcp_conn_get_stats,
10628c2ecf20Sopenharmony_ci	/* iscsi task/cmd helpers */
10638c2ecf20Sopenharmony_ci	.init_task		= iscsi_tcp_task_init,
10648c2ecf20Sopenharmony_ci	.xmit_task		= iscsi_tcp_task_xmit,
10658c2ecf20Sopenharmony_ci	.cleanup_task		= iscsi_tcp_cleanup_task,
10668c2ecf20Sopenharmony_ci	/* low level pdu helpers */
10678c2ecf20Sopenharmony_ci	.xmit_pdu		= iscsi_sw_tcp_pdu_xmit,
10688c2ecf20Sopenharmony_ci	.init_pdu		= iscsi_sw_tcp_pdu_init,
10698c2ecf20Sopenharmony_ci	.alloc_pdu		= iscsi_sw_tcp_pdu_alloc,
10708c2ecf20Sopenharmony_ci	/* recovery */
10718c2ecf20Sopenharmony_ci	.session_recovery_timedout = iscsi_session_recovery_timedout,
10728c2ecf20Sopenharmony_ci};
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_cistatic int __init iscsi_sw_tcp_init(void)
10758c2ecf20Sopenharmony_ci{
10768c2ecf20Sopenharmony_ci	if (iscsi_max_lun < 1) {
10778c2ecf20Sopenharmony_ci		printk(KERN_ERR "iscsi_tcp: Invalid max_lun value of %u\n",
10788c2ecf20Sopenharmony_ci		       iscsi_max_lun);
10798c2ecf20Sopenharmony_ci		return -EINVAL;
10808c2ecf20Sopenharmony_ci	}
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	iscsi_sw_tcp_scsi_transport = iscsi_register_transport(
10838c2ecf20Sopenharmony_ci						&iscsi_sw_tcp_transport);
10848c2ecf20Sopenharmony_ci	if (!iscsi_sw_tcp_scsi_transport)
10858c2ecf20Sopenharmony_ci		return -ENODEV;
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	return 0;
10888c2ecf20Sopenharmony_ci}
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_cistatic void __exit iscsi_sw_tcp_exit(void)
10918c2ecf20Sopenharmony_ci{
10928c2ecf20Sopenharmony_ci	iscsi_unregister_transport(&iscsi_sw_tcp_transport);
10938c2ecf20Sopenharmony_ci}
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_cimodule_init(iscsi_sw_tcp_init);
10968c2ecf20Sopenharmony_cimodule_exit(iscsi_sw_tcp_exit);
1097