162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * iSCSI Initiator over TCP/IP Data-Path 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 * See the file COPYING included with this distribution for more details. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Credits: 1462306a36Sopenharmony_ci * Christoph Hellwig 1562306a36Sopenharmony_ci * FUJITA Tomonori 1662306a36Sopenharmony_ci * Arne Redlich 1762306a36Sopenharmony_ci * Zhenyu Wang 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <crypto/hash.h> 2162306a36Sopenharmony_ci#include <linux/types.h> 2262306a36Sopenharmony_ci#include <linux/inet.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci#include <linux/sched/mm.h> 2562306a36Sopenharmony_ci#include <linux/file.h> 2662306a36Sopenharmony_ci#include <linux/blkdev.h> 2762306a36Sopenharmony_ci#include <linux/delay.h> 2862306a36Sopenharmony_ci#include <linux/kfifo.h> 2962306a36Sopenharmony_ci#include <linux/scatterlist.h> 3062306a36Sopenharmony_ci#include <linux/module.h> 3162306a36Sopenharmony_ci#include <linux/backing-dev.h> 3262306a36Sopenharmony_ci#include <net/tcp.h> 3362306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h> 3462306a36Sopenharmony_ci#include <scsi/scsi_device.h> 3562306a36Sopenharmony_ci#include <scsi/scsi_host.h> 3662306a36Sopenharmony_ci#include <scsi/scsi.h> 3762306a36Sopenharmony_ci#include <scsi/scsi_transport_iscsi.h> 3862306a36Sopenharmony_ci#include <trace/events/iscsi.h> 3962306a36Sopenharmony_ci#include <trace/events/sock.h> 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#include "iscsi_tcp.h" 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ciMODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, " 4462306a36Sopenharmony_ci "Dmitry Yusupov <dmitry_yus@yahoo.com>, " 4562306a36Sopenharmony_ci "Alex Aizman <itn780@yahoo.com>"); 4662306a36Sopenharmony_ciMODULE_DESCRIPTION("iSCSI/TCP data-path"); 4762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic struct scsi_transport_template *iscsi_sw_tcp_scsi_transport; 5062306a36Sopenharmony_cistatic const struct scsi_host_template iscsi_sw_tcp_sht; 5162306a36Sopenharmony_cistatic struct iscsi_transport iscsi_sw_tcp_transport; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic unsigned int iscsi_max_lun = ~0; 5462306a36Sopenharmony_cimodule_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic bool iscsi_recv_from_iscsi_q; 5762306a36Sopenharmony_cimodule_param_named(recv_from_iscsi_q, iscsi_recv_from_iscsi_q, bool, 0644); 5862306a36Sopenharmony_ciMODULE_PARM_DESC(recv_from_iscsi_q, "Set to true to read iSCSI data/headers from the iscsi_q workqueue. The default is false which will perform reads from the network softirq context."); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic int iscsi_sw_tcp_dbg; 6162306a36Sopenharmony_cimodule_param_named(debug_iscsi_tcp, iscsi_sw_tcp_dbg, int, 6262306a36Sopenharmony_ci S_IRUGO | S_IWUSR); 6362306a36Sopenharmony_ciMODULE_PARM_DESC(debug_iscsi_tcp, "Turn on debugging for iscsi_tcp module " 6462306a36Sopenharmony_ci "Set to 1 to turn on, and zero to turn off. Default is off."); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define ISCSI_SW_TCP_DBG(_conn, dbg_fmt, arg...) \ 6762306a36Sopenharmony_ci do { \ 6862306a36Sopenharmony_ci if (iscsi_sw_tcp_dbg) \ 6962306a36Sopenharmony_ci iscsi_conn_printk(KERN_INFO, _conn, \ 7062306a36Sopenharmony_ci "%s " dbg_fmt, \ 7162306a36Sopenharmony_ci __func__, ##arg); \ 7262306a36Sopenharmony_ci iscsi_dbg_trace(trace_iscsi_dbg_sw_tcp, \ 7362306a36Sopenharmony_ci &(_conn)->cls_conn->dev, \ 7462306a36Sopenharmony_ci "%s " dbg_fmt, __func__, ##arg);\ 7562306a36Sopenharmony_ci } while (0); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/** 7962306a36Sopenharmony_ci * iscsi_sw_tcp_recv - TCP receive in sendfile fashion 8062306a36Sopenharmony_ci * @rd_desc: read descriptor 8162306a36Sopenharmony_ci * @skb: socket buffer 8262306a36Sopenharmony_ci * @offset: offset in skb 8362306a36Sopenharmony_ci * @len: skb->len - offset 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_cistatic int iscsi_sw_tcp_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, 8662306a36Sopenharmony_ci unsigned int offset, size_t len) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct iscsi_conn *conn = rd_desc->arg.data; 8962306a36Sopenharmony_ci unsigned int consumed, total_consumed = 0; 9062306a36Sopenharmony_ci int status; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci ISCSI_SW_TCP_DBG(conn, "in %d bytes\n", skb->len - offset); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci do { 9562306a36Sopenharmony_ci status = 0; 9662306a36Sopenharmony_ci consumed = iscsi_tcp_recv_skb(conn, skb, offset, 0, &status); 9762306a36Sopenharmony_ci offset += consumed; 9862306a36Sopenharmony_ci total_consumed += consumed; 9962306a36Sopenharmony_ci } while (consumed != 0 && status != ISCSI_TCP_SKB_DONE); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci ISCSI_SW_TCP_DBG(conn, "read %d bytes status %d\n", 10262306a36Sopenharmony_ci skb->len - offset, status); 10362306a36Sopenharmony_ci return total_consumed; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/** 10762306a36Sopenharmony_ci * iscsi_sw_sk_state_check - check socket state 10862306a36Sopenharmony_ci * @sk: socket 10962306a36Sopenharmony_ci * 11062306a36Sopenharmony_ci * If the socket is in CLOSE or CLOSE_WAIT we should 11162306a36Sopenharmony_ci * not close the connection if there is still some 11262306a36Sopenharmony_ci * data pending. 11362306a36Sopenharmony_ci * 11462306a36Sopenharmony_ci * Must be called with sk_callback_lock. 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_cistatic inline int iscsi_sw_sk_state_check(struct sock *sk) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct iscsi_conn *conn = sk->sk_user_data; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if ((sk->sk_state == TCP_CLOSE_WAIT || sk->sk_state == TCP_CLOSE) && 12162306a36Sopenharmony_ci (conn->session->state != ISCSI_STATE_LOGGING_OUT) && 12262306a36Sopenharmony_ci !atomic_read(&sk->sk_rmem_alloc)) { 12362306a36Sopenharmony_ci ISCSI_SW_TCP_DBG(conn, "TCP_CLOSE|TCP_CLOSE_WAIT\n"); 12462306a36Sopenharmony_ci iscsi_conn_failure(conn, ISCSI_ERR_TCP_CONN_CLOSE); 12562306a36Sopenharmony_ci return -ECONNRESET; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void iscsi_sw_tcp_recv_data(struct iscsi_conn *conn) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 13362306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; 13462306a36Sopenharmony_ci struct sock *sk = tcp_sw_conn->sock->sk; 13562306a36Sopenharmony_ci read_descriptor_t rd_desc; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* 13862306a36Sopenharmony_ci * Use rd_desc to pass 'conn' to iscsi_tcp_recv. 13962306a36Sopenharmony_ci * We set count to 1 because we want the network layer to 14062306a36Sopenharmony_ci * hand us all the skbs that are available. iscsi_tcp_recv 14162306a36Sopenharmony_ci * handled pdus that cross buffers or pdus that still need data. 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_ci rd_desc.arg.data = conn; 14462306a36Sopenharmony_ci rd_desc.count = 1; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci tcp_read_sock(sk, &rd_desc, iscsi_sw_tcp_recv); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* If we had to (atomically) map a highmem page, 14962306a36Sopenharmony_ci * unmap it now. */ 15062306a36Sopenharmony_ci iscsi_tcp_segment_unmap(&tcp_conn->in.segment); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci iscsi_sw_sk_state_check(sk); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void iscsi_sw_tcp_recv_data_work(struct work_struct *work) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct iscsi_conn *conn = container_of(work, struct iscsi_conn, 15862306a36Sopenharmony_ci recvwork); 15962306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 16062306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; 16162306a36Sopenharmony_ci struct sock *sk = tcp_sw_conn->sock->sk; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci lock_sock(sk); 16462306a36Sopenharmony_ci iscsi_sw_tcp_recv_data(conn); 16562306a36Sopenharmony_ci release_sock(sk); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic void iscsi_sw_tcp_data_ready(struct sock *sk) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn; 17162306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn; 17262306a36Sopenharmony_ci struct iscsi_conn *conn; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci trace_sk_data_ready(sk); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci read_lock_bh(&sk->sk_callback_lock); 17762306a36Sopenharmony_ci conn = sk->sk_user_data; 17862306a36Sopenharmony_ci if (!conn) { 17962306a36Sopenharmony_ci read_unlock_bh(&sk->sk_callback_lock); 18062306a36Sopenharmony_ci return; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci tcp_conn = conn->dd_data; 18362306a36Sopenharmony_ci tcp_sw_conn = tcp_conn->dd_data; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (tcp_sw_conn->queue_recv) 18662306a36Sopenharmony_ci iscsi_conn_queue_recv(conn); 18762306a36Sopenharmony_ci else 18862306a36Sopenharmony_ci iscsi_sw_tcp_recv_data(conn); 18962306a36Sopenharmony_ci read_unlock_bh(&sk->sk_callback_lock); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic void iscsi_sw_tcp_state_change(struct sock *sk) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn; 19562306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn; 19662306a36Sopenharmony_ci struct iscsi_conn *conn; 19762306a36Sopenharmony_ci void (*old_state_change)(struct sock *); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci read_lock_bh(&sk->sk_callback_lock); 20062306a36Sopenharmony_ci conn = sk->sk_user_data; 20162306a36Sopenharmony_ci if (!conn) { 20262306a36Sopenharmony_ci read_unlock_bh(&sk->sk_callback_lock); 20362306a36Sopenharmony_ci return; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci iscsi_sw_sk_state_check(sk); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci tcp_conn = conn->dd_data; 20962306a36Sopenharmony_ci tcp_sw_conn = tcp_conn->dd_data; 21062306a36Sopenharmony_ci old_state_change = tcp_sw_conn->old_state_change; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci read_unlock_bh(&sk->sk_callback_lock); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci old_state_change(sk); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci/** 21862306a36Sopenharmony_ci * iscsi_sw_tcp_write_space - Called when more output buffer space is available 21962306a36Sopenharmony_ci * @sk: socket space is available for 22062306a36Sopenharmony_ci **/ 22162306a36Sopenharmony_cistatic void iscsi_sw_tcp_write_space(struct sock *sk) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct iscsi_conn *conn; 22462306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn; 22562306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn; 22662306a36Sopenharmony_ci void (*old_write_space)(struct sock *); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci read_lock_bh(&sk->sk_callback_lock); 22962306a36Sopenharmony_ci conn = sk->sk_user_data; 23062306a36Sopenharmony_ci if (!conn) { 23162306a36Sopenharmony_ci read_unlock_bh(&sk->sk_callback_lock); 23262306a36Sopenharmony_ci return; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci tcp_conn = conn->dd_data; 23662306a36Sopenharmony_ci tcp_sw_conn = tcp_conn->dd_data; 23762306a36Sopenharmony_ci old_write_space = tcp_sw_conn->old_write_space; 23862306a36Sopenharmony_ci read_unlock_bh(&sk->sk_callback_lock); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci old_write_space(sk); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci ISCSI_SW_TCP_DBG(conn, "iscsi_write_space\n"); 24362306a36Sopenharmony_ci iscsi_conn_queue_xmit(conn); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic void iscsi_sw_tcp_conn_set_callbacks(struct iscsi_conn *conn) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 24962306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; 25062306a36Sopenharmony_ci struct sock *sk = tcp_sw_conn->sock->sk; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* assign new callbacks */ 25362306a36Sopenharmony_ci write_lock_bh(&sk->sk_callback_lock); 25462306a36Sopenharmony_ci sk->sk_user_data = conn; 25562306a36Sopenharmony_ci tcp_sw_conn->old_data_ready = sk->sk_data_ready; 25662306a36Sopenharmony_ci tcp_sw_conn->old_state_change = sk->sk_state_change; 25762306a36Sopenharmony_ci tcp_sw_conn->old_write_space = sk->sk_write_space; 25862306a36Sopenharmony_ci sk->sk_data_ready = iscsi_sw_tcp_data_ready; 25962306a36Sopenharmony_ci sk->sk_state_change = iscsi_sw_tcp_state_change; 26062306a36Sopenharmony_ci sk->sk_write_space = iscsi_sw_tcp_write_space; 26162306a36Sopenharmony_ci write_unlock_bh(&sk->sk_callback_lock); 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic void 26562306a36Sopenharmony_ciiscsi_sw_tcp_conn_restore_callbacks(struct iscsi_conn *conn) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 26862306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; 26962306a36Sopenharmony_ci struct sock *sk = tcp_sw_conn->sock->sk; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* restore socket callbacks, see also: iscsi_conn_set_callbacks() */ 27262306a36Sopenharmony_ci write_lock_bh(&sk->sk_callback_lock); 27362306a36Sopenharmony_ci sk->sk_user_data = NULL; 27462306a36Sopenharmony_ci sk->sk_data_ready = tcp_sw_conn->old_data_ready; 27562306a36Sopenharmony_ci sk->sk_state_change = tcp_sw_conn->old_state_change; 27662306a36Sopenharmony_ci sk->sk_write_space = tcp_sw_conn->old_write_space; 27762306a36Sopenharmony_ci sk->sk_no_check_tx = 0; 27862306a36Sopenharmony_ci write_unlock_bh(&sk->sk_callback_lock); 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci/** 28262306a36Sopenharmony_ci * iscsi_sw_tcp_xmit_segment - transmit segment 28362306a36Sopenharmony_ci * @tcp_conn: the iSCSI TCP connection 28462306a36Sopenharmony_ci * @segment: the buffer to transmnit 28562306a36Sopenharmony_ci * 28662306a36Sopenharmony_ci * This function transmits as much of the buffer as 28762306a36Sopenharmony_ci * the network layer will accept, and returns the number of 28862306a36Sopenharmony_ci * bytes transmitted. 28962306a36Sopenharmony_ci * 29062306a36Sopenharmony_ci * If CRC hashing is enabled, the function will compute the 29162306a36Sopenharmony_ci * hash as it goes. When the entire segment has been transmitted, 29262306a36Sopenharmony_ci * it will retrieve the hash value and send it as well. 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_cistatic int iscsi_sw_tcp_xmit_segment(struct iscsi_tcp_conn *tcp_conn, 29562306a36Sopenharmony_ci struct iscsi_segment *segment) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; 29862306a36Sopenharmony_ci struct socket *sk = tcp_sw_conn->sock; 29962306a36Sopenharmony_ci unsigned int copied = 0; 30062306a36Sopenharmony_ci int r = 0; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci while (!iscsi_tcp_segment_done(tcp_conn, segment, 0, r)) { 30362306a36Sopenharmony_ci struct scatterlist *sg; 30462306a36Sopenharmony_ci struct msghdr msg = {}; 30562306a36Sopenharmony_ci struct bio_vec bv; 30662306a36Sopenharmony_ci unsigned int offset, copy; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci r = 0; 30962306a36Sopenharmony_ci offset = segment->copied; 31062306a36Sopenharmony_ci copy = segment->size - offset; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (segment->total_copied + segment->size < segment->total_size) 31362306a36Sopenharmony_ci msg.msg_flags |= MSG_MORE; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (tcp_sw_conn->queue_recv) 31662306a36Sopenharmony_ci msg.msg_flags |= MSG_DONTWAIT; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (!segment->data) { 31962306a36Sopenharmony_ci if (!tcp_conn->iscsi_conn->datadgst_en) 32062306a36Sopenharmony_ci msg.msg_flags |= MSG_SPLICE_PAGES; 32162306a36Sopenharmony_ci sg = segment->sg; 32262306a36Sopenharmony_ci offset += segment->sg_offset + sg->offset; 32362306a36Sopenharmony_ci bvec_set_page(&bv, sg_page(sg), copy, offset); 32462306a36Sopenharmony_ci } else { 32562306a36Sopenharmony_ci bvec_set_virt(&bv, segment->data + offset, copy); 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, &bv, 1, copy); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci r = sock_sendmsg(sk, &msg); 33062306a36Sopenharmony_ci if (r < 0) { 33162306a36Sopenharmony_ci iscsi_tcp_segment_unmap(segment); 33262306a36Sopenharmony_ci return r; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci copied += r; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci return copied; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci/** 34062306a36Sopenharmony_ci * iscsi_sw_tcp_xmit - TCP transmit 34162306a36Sopenharmony_ci * @conn: iscsi connection 34262306a36Sopenharmony_ci **/ 34362306a36Sopenharmony_cistatic int iscsi_sw_tcp_xmit(struct iscsi_conn *conn) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 34662306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; 34762306a36Sopenharmony_ci struct iscsi_segment *segment = &tcp_sw_conn->out.segment; 34862306a36Sopenharmony_ci unsigned int consumed = 0; 34962306a36Sopenharmony_ci int rc = 0; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci while (1) { 35262306a36Sopenharmony_ci rc = iscsi_sw_tcp_xmit_segment(tcp_conn, segment); 35362306a36Sopenharmony_ci /* 35462306a36Sopenharmony_ci * We may not have been able to send data because the conn 35562306a36Sopenharmony_ci * is getting stopped. libiscsi will know so propagate err 35662306a36Sopenharmony_ci * for it to do the right thing. 35762306a36Sopenharmony_ci */ 35862306a36Sopenharmony_ci if (rc == -EAGAIN) 35962306a36Sopenharmony_ci return rc; 36062306a36Sopenharmony_ci else if (rc < 0) { 36162306a36Sopenharmony_ci rc = ISCSI_ERR_XMIT_FAILED; 36262306a36Sopenharmony_ci goto error; 36362306a36Sopenharmony_ci } else if (rc == 0) 36462306a36Sopenharmony_ci break; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci consumed += rc; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (segment->total_copied >= segment->total_size) { 36962306a36Sopenharmony_ci if (segment->done != NULL) { 37062306a36Sopenharmony_ci rc = segment->done(tcp_conn, segment); 37162306a36Sopenharmony_ci if (rc != 0) 37262306a36Sopenharmony_ci goto error; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci ISCSI_SW_TCP_DBG(conn, "xmit %d bytes\n", consumed); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci conn->txdata_octets += consumed; 38062306a36Sopenharmony_ci return consumed; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cierror: 38362306a36Sopenharmony_ci /* Transmit error. We could initiate error recovery 38462306a36Sopenharmony_ci * here. */ 38562306a36Sopenharmony_ci ISCSI_SW_TCP_DBG(conn, "Error sending PDU, errno=%d\n", rc); 38662306a36Sopenharmony_ci iscsi_conn_failure(conn, rc); 38762306a36Sopenharmony_ci return -EIO; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci/** 39162306a36Sopenharmony_ci * iscsi_sw_tcp_xmit_qlen - return the number of bytes queued for xmit 39262306a36Sopenharmony_ci * @conn: iscsi connection 39362306a36Sopenharmony_ci */ 39462306a36Sopenharmony_cistatic inline int iscsi_sw_tcp_xmit_qlen(struct iscsi_conn *conn) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 39762306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; 39862306a36Sopenharmony_ci struct iscsi_segment *segment = &tcp_sw_conn->out.segment; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci return segment->total_copied - segment->total_size; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic int iscsi_sw_tcp_pdu_xmit(struct iscsi_task *task) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct iscsi_conn *conn = task->conn; 40662306a36Sopenharmony_ci unsigned int noreclaim_flag; 40762306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 40862306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; 40962306a36Sopenharmony_ci int rc = 0; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (!tcp_sw_conn->sock) { 41262306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 41362306a36Sopenharmony_ci "Transport not bound to socket!\n"); 41462306a36Sopenharmony_ci return -EINVAL; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci noreclaim_flag = memalloc_noreclaim_save(); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci while (iscsi_sw_tcp_xmit_qlen(conn)) { 42062306a36Sopenharmony_ci rc = iscsi_sw_tcp_xmit(conn); 42162306a36Sopenharmony_ci if (rc == 0) { 42262306a36Sopenharmony_ci rc = -EAGAIN; 42362306a36Sopenharmony_ci break; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci if (rc < 0) 42662306a36Sopenharmony_ci break; 42762306a36Sopenharmony_ci rc = 0; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci memalloc_noreclaim_restore(noreclaim_flag); 43162306a36Sopenharmony_ci return rc; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci/* 43562306a36Sopenharmony_ci * This is called when we're done sending the header. 43662306a36Sopenharmony_ci * Simply copy the data_segment to the send segment, and return. 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_cistatic int iscsi_sw_tcp_send_hdr_done(struct iscsi_tcp_conn *tcp_conn, 43962306a36Sopenharmony_ci struct iscsi_segment *segment) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci tcp_sw_conn->out.segment = tcp_sw_conn->out.data_segment; 44462306a36Sopenharmony_ci ISCSI_SW_TCP_DBG(tcp_conn->iscsi_conn, 44562306a36Sopenharmony_ci "Header done. Next segment size %u total_size %u\n", 44662306a36Sopenharmony_ci tcp_sw_conn->out.segment.size, 44762306a36Sopenharmony_ci tcp_sw_conn->out.segment.total_size); 44862306a36Sopenharmony_ci return 0; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic void iscsi_sw_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr, 45262306a36Sopenharmony_ci size_t hdrlen) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 45562306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci ISCSI_SW_TCP_DBG(conn, "%s\n", conn->hdrdgst_en ? 45862306a36Sopenharmony_ci "digest enabled" : "digest disabled"); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* Clear the data segment - needs to be filled in by the 46162306a36Sopenharmony_ci * caller using iscsi_tcp_send_data_prep() */ 46262306a36Sopenharmony_ci memset(&tcp_sw_conn->out.data_segment, 0, 46362306a36Sopenharmony_ci sizeof(struct iscsi_segment)); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* If header digest is enabled, compute the CRC and 46662306a36Sopenharmony_ci * place the digest into the same buffer. We make 46762306a36Sopenharmony_ci * sure that both iscsi_tcp_task and mtask have 46862306a36Sopenharmony_ci * sufficient room. 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_ci if (conn->hdrdgst_en) { 47162306a36Sopenharmony_ci iscsi_tcp_dgst_header(tcp_sw_conn->tx_hash, hdr, hdrlen, 47262306a36Sopenharmony_ci hdr + hdrlen); 47362306a36Sopenharmony_ci hdrlen += ISCSI_DIGEST_SIZE; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* Remember header pointer for later, when we need 47762306a36Sopenharmony_ci * to decide whether there's a payload to go along 47862306a36Sopenharmony_ci * with the header. */ 47962306a36Sopenharmony_ci tcp_sw_conn->out.hdr = hdr; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci iscsi_segment_init_linear(&tcp_sw_conn->out.segment, hdr, hdrlen, 48262306a36Sopenharmony_ci iscsi_sw_tcp_send_hdr_done, NULL); 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci/* 48662306a36Sopenharmony_ci * Prepare the send buffer for the payload data. 48762306a36Sopenharmony_ci * Padding and checksumming will all be taken care 48862306a36Sopenharmony_ci * of by the iscsi_segment routines. 48962306a36Sopenharmony_ci */ 49062306a36Sopenharmony_cistatic int 49162306a36Sopenharmony_ciiscsi_sw_tcp_send_data_prep(struct iscsi_conn *conn, struct scatterlist *sg, 49262306a36Sopenharmony_ci unsigned int count, unsigned int offset, 49362306a36Sopenharmony_ci unsigned int len) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 49662306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; 49762306a36Sopenharmony_ci struct ahash_request *tx_hash = NULL; 49862306a36Sopenharmony_ci unsigned int hdr_spec_len; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci ISCSI_SW_TCP_DBG(conn, "offset=%d, datalen=%d %s\n", offset, len, 50162306a36Sopenharmony_ci conn->datadgst_en ? 50262306a36Sopenharmony_ci "digest enabled" : "digest disabled"); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci /* Make sure the datalen matches what the caller 50562306a36Sopenharmony_ci said he would send. */ 50662306a36Sopenharmony_ci hdr_spec_len = ntoh24(tcp_sw_conn->out.hdr->dlength); 50762306a36Sopenharmony_ci WARN_ON(iscsi_padded(len) != iscsi_padded(hdr_spec_len)); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (conn->datadgst_en) 51062306a36Sopenharmony_ci tx_hash = tcp_sw_conn->tx_hash; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci return iscsi_segment_seek_sg(&tcp_sw_conn->out.data_segment, 51362306a36Sopenharmony_ci sg, count, offset, len, 51462306a36Sopenharmony_ci NULL, tx_hash); 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic void 51862306a36Sopenharmony_ciiscsi_sw_tcp_send_linear_data_prep(struct iscsi_conn *conn, void *data, 51962306a36Sopenharmony_ci size_t len) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 52262306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; 52362306a36Sopenharmony_ci struct ahash_request *tx_hash = NULL; 52462306a36Sopenharmony_ci unsigned int hdr_spec_len; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci ISCSI_SW_TCP_DBG(conn, "datalen=%zd %s\n", len, conn->datadgst_en ? 52762306a36Sopenharmony_ci "digest enabled" : "digest disabled"); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* Make sure the datalen matches what the caller 53062306a36Sopenharmony_ci said he would send. */ 53162306a36Sopenharmony_ci hdr_spec_len = ntoh24(tcp_sw_conn->out.hdr->dlength); 53262306a36Sopenharmony_ci WARN_ON(iscsi_padded(len) != iscsi_padded(hdr_spec_len)); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (conn->datadgst_en) 53562306a36Sopenharmony_ci tx_hash = tcp_sw_conn->tx_hash; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci iscsi_segment_init_linear(&tcp_sw_conn->out.data_segment, 53862306a36Sopenharmony_ci data, len, NULL, tx_hash); 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic int iscsi_sw_tcp_pdu_init(struct iscsi_task *task, 54262306a36Sopenharmony_ci unsigned int offset, unsigned int count) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct iscsi_conn *conn = task->conn; 54562306a36Sopenharmony_ci int err = 0; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci iscsi_sw_tcp_send_hdr_prep(conn, task->hdr, task->hdr_len); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (!count) 55062306a36Sopenharmony_ci return 0; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (!task->sc) 55362306a36Sopenharmony_ci iscsi_sw_tcp_send_linear_data_prep(conn, task->data, count); 55462306a36Sopenharmony_ci else { 55562306a36Sopenharmony_ci struct scsi_data_buffer *sdb = &task->sc->sdb; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci err = iscsi_sw_tcp_send_data_prep(conn, sdb->table.sgl, 55862306a36Sopenharmony_ci sdb->table.nents, offset, 55962306a36Sopenharmony_ci count); 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (err) { 56362306a36Sopenharmony_ci /* got invalid offset/len */ 56462306a36Sopenharmony_ci return -EIO; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci return 0; 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistatic int iscsi_sw_tcp_pdu_alloc(struct iscsi_task *task, uint8_t opcode) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci struct iscsi_tcp_task *tcp_task = task->dd_data; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci task->hdr = task->dd_data + sizeof(*tcp_task); 57462306a36Sopenharmony_ci task->hdr_max = sizeof(struct iscsi_sw_tcp_hdrbuf) - ISCSI_DIGEST_SIZE; 57562306a36Sopenharmony_ci return 0; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic struct iscsi_cls_conn * 57962306a36Sopenharmony_ciiscsi_sw_tcp_conn_create(struct iscsi_cls_session *cls_session, 58062306a36Sopenharmony_ci uint32_t conn_idx) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci struct iscsi_conn *conn; 58362306a36Sopenharmony_ci struct iscsi_cls_conn *cls_conn; 58462306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn; 58562306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn; 58662306a36Sopenharmony_ci struct crypto_ahash *tfm; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci cls_conn = iscsi_tcp_conn_setup(cls_session, sizeof(*tcp_sw_conn), 58962306a36Sopenharmony_ci conn_idx); 59062306a36Sopenharmony_ci if (!cls_conn) 59162306a36Sopenharmony_ci return NULL; 59262306a36Sopenharmony_ci conn = cls_conn->dd_data; 59362306a36Sopenharmony_ci tcp_conn = conn->dd_data; 59462306a36Sopenharmony_ci tcp_sw_conn = tcp_conn->dd_data; 59562306a36Sopenharmony_ci INIT_WORK(&conn->recvwork, iscsi_sw_tcp_recv_data_work); 59662306a36Sopenharmony_ci tcp_sw_conn->queue_recv = iscsi_recv_from_iscsi_q; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci mutex_init(&tcp_sw_conn->sock_lock); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci tfm = crypto_alloc_ahash("crc32c", 0, CRYPTO_ALG_ASYNC); 60162306a36Sopenharmony_ci if (IS_ERR(tfm)) 60262306a36Sopenharmony_ci goto free_conn; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci tcp_sw_conn->tx_hash = ahash_request_alloc(tfm, GFP_KERNEL); 60562306a36Sopenharmony_ci if (!tcp_sw_conn->tx_hash) 60662306a36Sopenharmony_ci goto free_tfm; 60762306a36Sopenharmony_ci ahash_request_set_callback(tcp_sw_conn->tx_hash, 0, NULL, NULL); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci tcp_sw_conn->rx_hash = ahash_request_alloc(tfm, GFP_KERNEL); 61062306a36Sopenharmony_ci if (!tcp_sw_conn->rx_hash) 61162306a36Sopenharmony_ci goto free_tx_hash; 61262306a36Sopenharmony_ci ahash_request_set_callback(tcp_sw_conn->rx_hash, 0, NULL, NULL); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci tcp_conn->rx_hash = tcp_sw_conn->rx_hash; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci return cls_conn; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cifree_tx_hash: 61962306a36Sopenharmony_ci ahash_request_free(tcp_sw_conn->tx_hash); 62062306a36Sopenharmony_cifree_tfm: 62162306a36Sopenharmony_ci crypto_free_ahash(tfm); 62262306a36Sopenharmony_cifree_conn: 62362306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 62462306a36Sopenharmony_ci "Could not create connection due to crc32c " 62562306a36Sopenharmony_ci "loading error. Make sure the crc32c " 62662306a36Sopenharmony_ci "module is built as a module or into the " 62762306a36Sopenharmony_ci "kernel\n"); 62862306a36Sopenharmony_ci iscsi_tcp_conn_teardown(cls_conn); 62962306a36Sopenharmony_ci return NULL; 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistatic void iscsi_sw_tcp_release_conn(struct iscsi_conn *conn) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 63562306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; 63662306a36Sopenharmony_ci struct socket *sock = tcp_sw_conn->sock; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci /* 63962306a36Sopenharmony_ci * The iscsi transport class will make sure we are not called in 64062306a36Sopenharmony_ci * parallel with start, stop, bind and destroys. However, this can be 64162306a36Sopenharmony_ci * called twice if userspace does a stop then a destroy. 64262306a36Sopenharmony_ci */ 64362306a36Sopenharmony_ci if (!sock) 64462306a36Sopenharmony_ci return; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* 64762306a36Sopenharmony_ci * Make sure we start socket shutdown now in case userspace is up 64862306a36Sopenharmony_ci * but delayed in releasing the socket. 64962306a36Sopenharmony_ci */ 65062306a36Sopenharmony_ci kernel_sock_shutdown(sock, SHUT_RDWR); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci sock_hold(sock->sk); 65362306a36Sopenharmony_ci iscsi_sw_tcp_conn_restore_callbacks(conn); 65462306a36Sopenharmony_ci sock_put(sock->sk); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci iscsi_suspend_rx(conn); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci mutex_lock(&tcp_sw_conn->sock_lock); 65962306a36Sopenharmony_ci tcp_sw_conn->sock = NULL; 66062306a36Sopenharmony_ci mutex_unlock(&tcp_sw_conn->sock_lock); 66162306a36Sopenharmony_ci sockfd_put(sock); 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_cistatic void iscsi_sw_tcp_conn_destroy(struct iscsi_cls_conn *cls_conn) 66562306a36Sopenharmony_ci{ 66662306a36Sopenharmony_ci struct iscsi_conn *conn = cls_conn->dd_data; 66762306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 66862306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci iscsi_sw_tcp_release_conn(conn); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci ahash_request_free(tcp_sw_conn->rx_hash); 67362306a36Sopenharmony_ci if (tcp_sw_conn->tx_hash) { 67462306a36Sopenharmony_ci struct crypto_ahash *tfm; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci tfm = crypto_ahash_reqtfm(tcp_sw_conn->tx_hash); 67762306a36Sopenharmony_ci ahash_request_free(tcp_sw_conn->tx_hash); 67862306a36Sopenharmony_ci crypto_free_ahash(tfm); 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci iscsi_tcp_conn_teardown(cls_conn); 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic void iscsi_sw_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci struct iscsi_conn *conn = cls_conn->dd_data; 68762306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 68862306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; 68962306a36Sopenharmony_ci struct socket *sock = tcp_sw_conn->sock; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci /* userspace may have goofed up and not bound us */ 69262306a36Sopenharmony_ci if (!sock) 69362306a36Sopenharmony_ci return; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci sock->sk->sk_err = EIO; 69662306a36Sopenharmony_ci wake_up_interruptible(sk_sleep(sock->sk)); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci /* stop xmit side */ 69962306a36Sopenharmony_ci iscsi_suspend_tx(conn); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci /* stop recv side and release socket */ 70262306a36Sopenharmony_ci iscsi_sw_tcp_release_conn(conn); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci iscsi_conn_stop(cls_conn, flag); 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_cistatic int 70862306a36Sopenharmony_ciiscsi_sw_tcp_conn_bind(struct iscsi_cls_session *cls_session, 70962306a36Sopenharmony_ci struct iscsi_cls_conn *cls_conn, uint64_t transport_eph, 71062306a36Sopenharmony_ci int is_leading) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci struct iscsi_conn *conn = cls_conn->dd_data; 71362306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 71462306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; 71562306a36Sopenharmony_ci struct sock *sk; 71662306a36Sopenharmony_ci struct socket *sock; 71762306a36Sopenharmony_ci int err; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci /* lookup for existing socket */ 72062306a36Sopenharmony_ci sock = sockfd_lookup((int)transport_eph, &err); 72162306a36Sopenharmony_ci if (!sock) { 72262306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 72362306a36Sopenharmony_ci "sockfd_lookup failed %d\n", err); 72462306a36Sopenharmony_ci return -EEXIST; 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci err = -EINVAL; 72862306a36Sopenharmony_ci if (!sk_is_tcp(sock->sk)) 72962306a36Sopenharmony_ci goto free_socket; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci err = iscsi_conn_bind(cls_session, cls_conn, is_leading); 73262306a36Sopenharmony_ci if (err) 73362306a36Sopenharmony_ci goto free_socket; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci mutex_lock(&tcp_sw_conn->sock_lock); 73662306a36Sopenharmony_ci /* bind iSCSI connection and socket */ 73762306a36Sopenharmony_ci tcp_sw_conn->sock = sock; 73862306a36Sopenharmony_ci mutex_unlock(&tcp_sw_conn->sock_lock); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* setup Socket parameters */ 74162306a36Sopenharmony_ci sk = sock->sk; 74262306a36Sopenharmony_ci sk->sk_reuse = SK_CAN_REUSE; 74362306a36Sopenharmony_ci sk->sk_sndtimeo = 15 * HZ; /* FIXME: make it configurable */ 74462306a36Sopenharmony_ci sk->sk_allocation = GFP_ATOMIC; 74562306a36Sopenharmony_ci sk->sk_use_task_frag = false; 74662306a36Sopenharmony_ci sk_set_memalloc(sk); 74762306a36Sopenharmony_ci sock_no_linger(sk); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci iscsi_sw_tcp_conn_set_callbacks(conn); 75062306a36Sopenharmony_ci /* 75162306a36Sopenharmony_ci * set receive state machine into initial state 75262306a36Sopenharmony_ci */ 75362306a36Sopenharmony_ci iscsi_tcp_hdr_recv_prep(tcp_conn); 75462306a36Sopenharmony_ci return 0; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cifree_socket: 75762306a36Sopenharmony_ci sockfd_put(sock); 75862306a36Sopenharmony_ci return err; 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_cistatic int iscsi_sw_tcp_conn_set_param(struct iscsi_cls_conn *cls_conn, 76262306a36Sopenharmony_ci enum iscsi_param param, char *buf, 76362306a36Sopenharmony_ci int buflen) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci struct iscsi_conn *conn = cls_conn->dd_data; 76662306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 76762306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci switch(param) { 77062306a36Sopenharmony_ci case ISCSI_PARAM_HDRDGST_EN: 77162306a36Sopenharmony_ci iscsi_set_param(cls_conn, param, buf, buflen); 77262306a36Sopenharmony_ci break; 77362306a36Sopenharmony_ci case ISCSI_PARAM_DATADGST_EN: 77462306a36Sopenharmony_ci mutex_lock(&tcp_sw_conn->sock_lock); 77562306a36Sopenharmony_ci if (!tcp_sw_conn->sock) { 77662306a36Sopenharmony_ci mutex_unlock(&tcp_sw_conn->sock_lock); 77762306a36Sopenharmony_ci return -ENOTCONN; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci iscsi_set_param(cls_conn, param, buf, buflen); 78062306a36Sopenharmony_ci mutex_unlock(&tcp_sw_conn->sock_lock); 78162306a36Sopenharmony_ci break; 78262306a36Sopenharmony_ci case ISCSI_PARAM_MAX_R2T: 78362306a36Sopenharmony_ci return iscsi_tcp_set_max_r2t(conn, buf); 78462306a36Sopenharmony_ci default: 78562306a36Sopenharmony_ci return iscsi_set_param(cls_conn, param, buf, buflen); 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci return 0; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cistatic int iscsi_sw_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn, 79262306a36Sopenharmony_ci enum iscsi_param param, char *buf) 79362306a36Sopenharmony_ci{ 79462306a36Sopenharmony_ci struct iscsi_conn *conn = cls_conn->dd_data; 79562306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn; 79662306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn; 79762306a36Sopenharmony_ci struct sockaddr_in6 addr; 79862306a36Sopenharmony_ci struct socket *sock; 79962306a36Sopenharmony_ci int rc; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci switch(param) { 80262306a36Sopenharmony_ci case ISCSI_PARAM_CONN_PORT: 80362306a36Sopenharmony_ci case ISCSI_PARAM_CONN_ADDRESS: 80462306a36Sopenharmony_ci case ISCSI_PARAM_LOCAL_PORT: 80562306a36Sopenharmony_ci spin_lock_bh(&conn->session->frwd_lock); 80662306a36Sopenharmony_ci if (!conn->session->leadconn) { 80762306a36Sopenharmony_ci spin_unlock_bh(&conn->session->frwd_lock); 80862306a36Sopenharmony_ci return -ENOTCONN; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci /* 81162306a36Sopenharmony_ci * The conn has been setup and bound, so just grab a ref 81262306a36Sopenharmony_ci * incase a destroy runs while we are in the net layer. 81362306a36Sopenharmony_ci */ 81462306a36Sopenharmony_ci iscsi_get_conn(conn->cls_conn); 81562306a36Sopenharmony_ci spin_unlock_bh(&conn->session->frwd_lock); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci tcp_conn = conn->dd_data; 81862306a36Sopenharmony_ci tcp_sw_conn = tcp_conn->dd_data; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci mutex_lock(&tcp_sw_conn->sock_lock); 82162306a36Sopenharmony_ci sock = tcp_sw_conn->sock; 82262306a36Sopenharmony_ci if (!sock) { 82362306a36Sopenharmony_ci rc = -ENOTCONN; 82462306a36Sopenharmony_ci goto sock_unlock; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci if (param == ISCSI_PARAM_LOCAL_PORT) 82862306a36Sopenharmony_ci rc = kernel_getsockname(sock, 82962306a36Sopenharmony_ci (struct sockaddr *)&addr); 83062306a36Sopenharmony_ci else 83162306a36Sopenharmony_ci rc = kernel_getpeername(sock, 83262306a36Sopenharmony_ci (struct sockaddr *)&addr); 83362306a36Sopenharmony_cisock_unlock: 83462306a36Sopenharmony_ci mutex_unlock(&tcp_sw_conn->sock_lock); 83562306a36Sopenharmony_ci iscsi_put_conn(conn->cls_conn); 83662306a36Sopenharmony_ci if (rc < 0) 83762306a36Sopenharmony_ci return rc; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci return iscsi_conn_get_addr_param((struct sockaddr_storage *) 84062306a36Sopenharmony_ci &addr, param, buf); 84162306a36Sopenharmony_ci default: 84262306a36Sopenharmony_ci return iscsi_conn_get_param(cls_conn, param, buf); 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci return 0; 84662306a36Sopenharmony_ci} 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_cistatic int iscsi_sw_tcp_host_get_param(struct Scsi_Host *shost, 84962306a36Sopenharmony_ci enum iscsi_host_param param, char *buf) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci struct iscsi_sw_tcp_host *tcp_sw_host = iscsi_host_priv(shost); 85262306a36Sopenharmony_ci struct iscsi_session *session; 85362306a36Sopenharmony_ci struct iscsi_conn *conn; 85462306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn; 85562306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn; 85662306a36Sopenharmony_ci struct sockaddr_in6 addr; 85762306a36Sopenharmony_ci struct socket *sock; 85862306a36Sopenharmony_ci int rc; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci switch (param) { 86162306a36Sopenharmony_ci case ISCSI_HOST_PARAM_IPADDRESS: 86262306a36Sopenharmony_ci session = tcp_sw_host->session; 86362306a36Sopenharmony_ci if (!session) 86462306a36Sopenharmony_ci return -ENOTCONN; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 86762306a36Sopenharmony_ci conn = session->leadconn; 86862306a36Sopenharmony_ci if (!conn) { 86962306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 87062306a36Sopenharmony_ci return -ENOTCONN; 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci tcp_conn = conn->dd_data; 87362306a36Sopenharmony_ci tcp_sw_conn = tcp_conn->dd_data; 87462306a36Sopenharmony_ci /* 87562306a36Sopenharmony_ci * The conn has been setup and bound, so just grab a ref 87662306a36Sopenharmony_ci * incase a destroy runs while we are in the net layer. 87762306a36Sopenharmony_ci */ 87862306a36Sopenharmony_ci iscsi_get_conn(conn->cls_conn); 87962306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci mutex_lock(&tcp_sw_conn->sock_lock); 88262306a36Sopenharmony_ci sock = tcp_sw_conn->sock; 88362306a36Sopenharmony_ci if (!sock) 88462306a36Sopenharmony_ci rc = -ENOTCONN; 88562306a36Sopenharmony_ci else 88662306a36Sopenharmony_ci rc = kernel_getsockname(sock, (struct sockaddr *)&addr); 88762306a36Sopenharmony_ci mutex_unlock(&tcp_sw_conn->sock_lock); 88862306a36Sopenharmony_ci iscsi_put_conn(conn->cls_conn); 88962306a36Sopenharmony_ci if (rc < 0) 89062306a36Sopenharmony_ci return rc; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci return iscsi_conn_get_addr_param((struct sockaddr_storage *) 89362306a36Sopenharmony_ci &addr, 89462306a36Sopenharmony_ci (enum iscsi_param)param, buf); 89562306a36Sopenharmony_ci default: 89662306a36Sopenharmony_ci return iscsi_host_get_param(shost, param, buf); 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci return 0; 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_cistatic void 90362306a36Sopenharmony_ciiscsi_sw_tcp_conn_get_stats(struct iscsi_cls_conn *cls_conn, 90462306a36Sopenharmony_ci struct iscsi_stats *stats) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci struct iscsi_conn *conn = cls_conn->dd_data; 90762306a36Sopenharmony_ci struct iscsi_tcp_conn *tcp_conn = conn->dd_data; 90862306a36Sopenharmony_ci struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci stats->custom_length = 3; 91162306a36Sopenharmony_ci strcpy(stats->custom[0].desc, "tx_sendpage_failures"); 91262306a36Sopenharmony_ci stats->custom[0].value = tcp_sw_conn->sendpage_failures_cnt; 91362306a36Sopenharmony_ci strcpy(stats->custom[1].desc, "rx_discontiguous_hdr"); 91462306a36Sopenharmony_ci stats->custom[1].value = tcp_sw_conn->discontiguous_hdr_cnt; 91562306a36Sopenharmony_ci strcpy(stats->custom[2].desc, "eh_abort_cnt"); 91662306a36Sopenharmony_ci stats->custom[2].value = conn->eh_abort_cnt; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci iscsi_tcp_conn_get_stats(cls_conn, stats); 91962306a36Sopenharmony_ci} 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_cistatic struct iscsi_cls_session * 92262306a36Sopenharmony_ciiscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max, 92362306a36Sopenharmony_ci uint16_t qdepth, uint32_t initial_cmdsn) 92462306a36Sopenharmony_ci{ 92562306a36Sopenharmony_ci struct iscsi_cls_session *cls_session; 92662306a36Sopenharmony_ci struct iscsi_session *session; 92762306a36Sopenharmony_ci struct iscsi_sw_tcp_host *tcp_sw_host; 92862306a36Sopenharmony_ci struct Scsi_Host *shost; 92962306a36Sopenharmony_ci int rc; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci if (ep) { 93262306a36Sopenharmony_ci printk(KERN_ERR "iscsi_tcp: invalid ep %p.\n", ep); 93362306a36Sopenharmony_ci return NULL; 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci shost = iscsi_host_alloc(&iscsi_sw_tcp_sht, 93762306a36Sopenharmony_ci sizeof(struct iscsi_sw_tcp_host), 1); 93862306a36Sopenharmony_ci if (!shost) 93962306a36Sopenharmony_ci return NULL; 94062306a36Sopenharmony_ci shost->transportt = iscsi_sw_tcp_scsi_transport; 94162306a36Sopenharmony_ci shost->cmd_per_lun = qdepth; 94262306a36Sopenharmony_ci shost->max_lun = iscsi_max_lun; 94362306a36Sopenharmony_ci shost->max_id = 0; 94462306a36Sopenharmony_ci shost->max_channel = 0; 94562306a36Sopenharmony_ci shost->max_cmd_len = SCSI_MAX_VARLEN_CDB_SIZE; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci rc = iscsi_host_get_max_scsi_cmds(shost, cmds_max); 94862306a36Sopenharmony_ci if (rc < 0) 94962306a36Sopenharmony_ci goto free_host; 95062306a36Sopenharmony_ci shost->can_queue = rc; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci if (iscsi_host_add(shost, NULL)) 95362306a36Sopenharmony_ci goto free_host; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci cls_session = iscsi_session_setup(&iscsi_sw_tcp_transport, shost, 95662306a36Sopenharmony_ci cmds_max, 0, 95762306a36Sopenharmony_ci sizeof(struct iscsi_tcp_task) + 95862306a36Sopenharmony_ci sizeof(struct iscsi_sw_tcp_hdrbuf), 95962306a36Sopenharmony_ci initial_cmdsn, 0); 96062306a36Sopenharmony_ci if (!cls_session) 96162306a36Sopenharmony_ci goto remove_host; 96262306a36Sopenharmony_ci session = cls_session->dd_data; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci if (iscsi_tcp_r2tpool_alloc(session)) 96562306a36Sopenharmony_ci goto remove_session; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci /* We are now fully setup so expose the session to sysfs. */ 96862306a36Sopenharmony_ci tcp_sw_host = iscsi_host_priv(shost); 96962306a36Sopenharmony_ci tcp_sw_host->session = session; 97062306a36Sopenharmony_ci return cls_session; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ciremove_session: 97362306a36Sopenharmony_ci iscsi_session_teardown(cls_session); 97462306a36Sopenharmony_ciremove_host: 97562306a36Sopenharmony_ci iscsi_host_remove(shost, false); 97662306a36Sopenharmony_cifree_host: 97762306a36Sopenharmony_ci iscsi_host_free(shost); 97862306a36Sopenharmony_ci return NULL; 97962306a36Sopenharmony_ci} 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_cistatic void iscsi_sw_tcp_session_destroy(struct iscsi_cls_session *cls_session) 98262306a36Sopenharmony_ci{ 98362306a36Sopenharmony_ci struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); 98462306a36Sopenharmony_ci struct iscsi_session *session = cls_session->dd_data; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if (WARN_ON_ONCE(session->leadconn)) 98762306a36Sopenharmony_ci return; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci iscsi_session_remove(cls_session); 99062306a36Sopenharmony_ci /* 99162306a36Sopenharmony_ci * Our get_host_param needs to access the session, so remove the 99262306a36Sopenharmony_ci * host from sysfs before freeing the session to make sure userspace 99362306a36Sopenharmony_ci * is no longer accessing the callout. 99462306a36Sopenharmony_ci */ 99562306a36Sopenharmony_ci iscsi_host_remove(shost, false); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci iscsi_tcp_r2tpool_free(cls_session->dd_data); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci iscsi_session_free(cls_session); 100062306a36Sopenharmony_ci iscsi_host_free(shost); 100162306a36Sopenharmony_ci} 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_cistatic umode_t iscsi_sw_tcp_attr_is_visible(int param_type, int param) 100462306a36Sopenharmony_ci{ 100562306a36Sopenharmony_ci switch (param_type) { 100662306a36Sopenharmony_ci case ISCSI_HOST_PARAM: 100762306a36Sopenharmony_ci switch (param) { 100862306a36Sopenharmony_ci case ISCSI_HOST_PARAM_NETDEV_NAME: 100962306a36Sopenharmony_ci case ISCSI_HOST_PARAM_HWADDRESS: 101062306a36Sopenharmony_ci case ISCSI_HOST_PARAM_IPADDRESS: 101162306a36Sopenharmony_ci case ISCSI_HOST_PARAM_INITIATOR_NAME: 101262306a36Sopenharmony_ci return S_IRUGO; 101362306a36Sopenharmony_ci default: 101462306a36Sopenharmony_ci return 0; 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci case ISCSI_PARAM: 101762306a36Sopenharmony_ci switch (param) { 101862306a36Sopenharmony_ci case ISCSI_PARAM_MAX_RECV_DLENGTH: 101962306a36Sopenharmony_ci case ISCSI_PARAM_MAX_XMIT_DLENGTH: 102062306a36Sopenharmony_ci case ISCSI_PARAM_HDRDGST_EN: 102162306a36Sopenharmony_ci case ISCSI_PARAM_DATADGST_EN: 102262306a36Sopenharmony_ci case ISCSI_PARAM_CONN_ADDRESS: 102362306a36Sopenharmony_ci case ISCSI_PARAM_CONN_PORT: 102462306a36Sopenharmony_ci case ISCSI_PARAM_LOCAL_PORT: 102562306a36Sopenharmony_ci case ISCSI_PARAM_EXP_STATSN: 102662306a36Sopenharmony_ci case ISCSI_PARAM_PERSISTENT_ADDRESS: 102762306a36Sopenharmony_ci case ISCSI_PARAM_PERSISTENT_PORT: 102862306a36Sopenharmony_ci case ISCSI_PARAM_PING_TMO: 102962306a36Sopenharmony_ci case ISCSI_PARAM_RECV_TMO: 103062306a36Sopenharmony_ci case ISCSI_PARAM_INITIAL_R2T_EN: 103162306a36Sopenharmony_ci case ISCSI_PARAM_MAX_R2T: 103262306a36Sopenharmony_ci case ISCSI_PARAM_IMM_DATA_EN: 103362306a36Sopenharmony_ci case ISCSI_PARAM_FIRST_BURST: 103462306a36Sopenharmony_ci case ISCSI_PARAM_MAX_BURST: 103562306a36Sopenharmony_ci case ISCSI_PARAM_PDU_INORDER_EN: 103662306a36Sopenharmony_ci case ISCSI_PARAM_DATASEQ_INORDER_EN: 103762306a36Sopenharmony_ci case ISCSI_PARAM_ERL: 103862306a36Sopenharmony_ci case ISCSI_PARAM_TARGET_NAME: 103962306a36Sopenharmony_ci case ISCSI_PARAM_TPGT: 104062306a36Sopenharmony_ci case ISCSI_PARAM_USERNAME: 104162306a36Sopenharmony_ci case ISCSI_PARAM_PASSWORD: 104262306a36Sopenharmony_ci case ISCSI_PARAM_USERNAME_IN: 104362306a36Sopenharmony_ci case ISCSI_PARAM_PASSWORD_IN: 104462306a36Sopenharmony_ci case ISCSI_PARAM_FAST_ABORT: 104562306a36Sopenharmony_ci case ISCSI_PARAM_ABORT_TMO: 104662306a36Sopenharmony_ci case ISCSI_PARAM_LU_RESET_TMO: 104762306a36Sopenharmony_ci case ISCSI_PARAM_TGT_RESET_TMO: 104862306a36Sopenharmony_ci case ISCSI_PARAM_IFACE_NAME: 104962306a36Sopenharmony_ci case ISCSI_PARAM_INITIATOR_NAME: 105062306a36Sopenharmony_ci return S_IRUGO; 105162306a36Sopenharmony_ci default: 105262306a36Sopenharmony_ci return 0; 105362306a36Sopenharmony_ci } 105462306a36Sopenharmony_ci } 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci return 0; 105762306a36Sopenharmony_ci} 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_cistatic int iscsi_sw_tcp_slave_configure(struct scsi_device *sdev) 106062306a36Sopenharmony_ci{ 106162306a36Sopenharmony_ci struct iscsi_sw_tcp_host *tcp_sw_host = iscsi_host_priv(sdev->host); 106262306a36Sopenharmony_ci struct iscsi_session *session = tcp_sw_host->session; 106362306a36Sopenharmony_ci struct iscsi_conn *conn = session->leadconn; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci if (conn->datadgst_en) 106662306a36Sopenharmony_ci blk_queue_flag_set(QUEUE_FLAG_STABLE_WRITES, 106762306a36Sopenharmony_ci sdev->request_queue); 106862306a36Sopenharmony_ci blk_queue_dma_alignment(sdev->request_queue, 0); 106962306a36Sopenharmony_ci return 0; 107062306a36Sopenharmony_ci} 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_cistatic const struct scsi_host_template iscsi_sw_tcp_sht = { 107362306a36Sopenharmony_ci .module = THIS_MODULE, 107462306a36Sopenharmony_ci .name = "iSCSI Initiator over TCP/IP", 107562306a36Sopenharmony_ci .queuecommand = iscsi_queuecommand, 107662306a36Sopenharmony_ci .change_queue_depth = scsi_change_queue_depth, 107762306a36Sopenharmony_ci .can_queue = ISCSI_TOTAL_CMDS_MAX, 107862306a36Sopenharmony_ci .sg_tablesize = 4096, 107962306a36Sopenharmony_ci .max_sectors = 0xFFFF, 108062306a36Sopenharmony_ci .cmd_per_lun = ISCSI_DEF_CMD_PER_LUN, 108162306a36Sopenharmony_ci .eh_timed_out = iscsi_eh_cmd_timed_out, 108262306a36Sopenharmony_ci .eh_abort_handler = iscsi_eh_abort, 108362306a36Sopenharmony_ci .eh_device_reset_handler= iscsi_eh_device_reset, 108462306a36Sopenharmony_ci .eh_target_reset_handler = iscsi_eh_recover_target, 108562306a36Sopenharmony_ci .dma_boundary = PAGE_SIZE - 1, 108662306a36Sopenharmony_ci .slave_configure = iscsi_sw_tcp_slave_configure, 108762306a36Sopenharmony_ci .proc_name = "iscsi_tcp", 108862306a36Sopenharmony_ci .this_id = -1, 108962306a36Sopenharmony_ci .track_queue_depth = 1, 109062306a36Sopenharmony_ci .cmd_size = sizeof(struct iscsi_cmd), 109162306a36Sopenharmony_ci}; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_cistatic struct iscsi_transport iscsi_sw_tcp_transport = { 109462306a36Sopenharmony_ci .owner = THIS_MODULE, 109562306a36Sopenharmony_ci .name = "tcp", 109662306a36Sopenharmony_ci .caps = CAP_RECOVERY_L0 | CAP_MULTI_R2T | CAP_HDRDGST 109762306a36Sopenharmony_ci | CAP_DATADGST, 109862306a36Sopenharmony_ci /* session management */ 109962306a36Sopenharmony_ci .create_session = iscsi_sw_tcp_session_create, 110062306a36Sopenharmony_ci .destroy_session = iscsi_sw_tcp_session_destroy, 110162306a36Sopenharmony_ci /* connection management */ 110262306a36Sopenharmony_ci .create_conn = iscsi_sw_tcp_conn_create, 110362306a36Sopenharmony_ci .bind_conn = iscsi_sw_tcp_conn_bind, 110462306a36Sopenharmony_ci .destroy_conn = iscsi_sw_tcp_conn_destroy, 110562306a36Sopenharmony_ci .attr_is_visible = iscsi_sw_tcp_attr_is_visible, 110662306a36Sopenharmony_ci .set_param = iscsi_sw_tcp_conn_set_param, 110762306a36Sopenharmony_ci .get_conn_param = iscsi_sw_tcp_conn_get_param, 110862306a36Sopenharmony_ci .get_session_param = iscsi_session_get_param, 110962306a36Sopenharmony_ci .start_conn = iscsi_conn_start, 111062306a36Sopenharmony_ci .stop_conn = iscsi_sw_tcp_conn_stop, 111162306a36Sopenharmony_ci /* iscsi host params */ 111262306a36Sopenharmony_ci .get_host_param = iscsi_sw_tcp_host_get_param, 111362306a36Sopenharmony_ci .set_host_param = iscsi_host_set_param, 111462306a36Sopenharmony_ci /* IO */ 111562306a36Sopenharmony_ci .send_pdu = iscsi_conn_send_pdu, 111662306a36Sopenharmony_ci .get_stats = iscsi_sw_tcp_conn_get_stats, 111762306a36Sopenharmony_ci /* iscsi task/cmd helpers */ 111862306a36Sopenharmony_ci .init_task = iscsi_tcp_task_init, 111962306a36Sopenharmony_ci .xmit_task = iscsi_tcp_task_xmit, 112062306a36Sopenharmony_ci .cleanup_task = iscsi_tcp_cleanup_task, 112162306a36Sopenharmony_ci /* low level pdu helpers */ 112262306a36Sopenharmony_ci .xmit_pdu = iscsi_sw_tcp_pdu_xmit, 112362306a36Sopenharmony_ci .init_pdu = iscsi_sw_tcp_pdu_init, 112462306a36Sopenharmony_ci .alloc_pdu = iscsi_sw_tcp_pdu_alloc, 112562306a36Sopenharmony_ci /* recovery */ 112662306a36Sopenharmony_ci .session_recovery_timedout = iscsi_session_recovery_timedout, 112762306a36Sopenharmony_ci}; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_cistatic int __init iscsi_sw_tcp_init(void) 113062306a36Sopenharmony_ci{ 113162306a36Sopenharmony_ci if (iscsi_max_lun < 1) { 113262306a36Sopenharmony_ci printk(KERN_ERR "iscsi_tcp: Invalid max_lun value of %u\n", 113362306a36Sopenharmony_ci iscsi_max_lun); 113462306a36Sopenharmony_ci return -EINVAL; 113562306a36Sopenharmony_ci } 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci iscsi_sw_tcp_scsi_transport = iscsi_register_transport( 113862306a36Sopenharmony_ci &iscsi_sw_tcp_transport); 113962306a36Sopenharmony_ci if (!iscsi_sw_tcp_scsi_transport) 114062306a36Sopenharmony_ci return -ENODEV; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci return 0; 114362306a36Sopenharmony_ci} 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_cistatic void __exit iscsi_sw_tcp_exit(void) 114662306a36Sopenharmony_ci{ 114762306a36Sopenharmony_ci iscsi_unregister_transport(&iscsi_sw_tcp_transport); 114862306a36Sopenharmony_ci} 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_cimodule_init(iscsi_sw_tcp_init); 115162306a36Sopenharmony_cimodule_exit(iscsi_sw_tcp_exit); 1152