// SPDX-License-Identifier: GPL-2.0-or-later /* * iSCSI over TCP/IP Data-Path lib * * Copyright (C) 2004 Dmitry Yusupov * Copyright (C) 2004 Alex Aizman * Copyright (C) 2005 - 2006 Mike Christie * Copyright (C) 2006 Red Hat, Inc. All rights reserved. * maintained by [email protected] * * Credits: * Christoph Hellwig * FUJITA Tomonori * Arne Redlich * Zhenyu Wang */ #include <crypto/hash.h> #include <linux/types.h> #include <linux/list.h> #include <linux/inet.h> #include <linux/slab.h> #include <linux/file.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/kfifo.h> #include <linux/scatterlist.h> #include <linux/module.h> #include <net/tcp.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> #include <scsi/scsi.h> #include <scsi/scsi_transport_iscsi.h> #include <trace/events/iscsi.h> #include "iscsi_tcp.h" MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …; static int iscsi_dbg_libtcp; module_param_named(debug_libiscsi_tcp, iscsi_dbg_libtcp, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(…) …; #define ISCSI_DBG_TCP(_conn, dbg_fmt, arg...) … static int iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn, struct iscsi_segment *segment); /* * Scatterlist handling: inside the iscsi_segment, we * remember an index into the scatterlist, and set data/size * to the current scatterlist entry. For highmem pages, we * kmap as needed. * * Note that the page is unmapped when we return from * TCP's data_ready handler, so we may end up mapping and * unmapping the same page repeatedly. The whole reason * for this is that we shouldn't keep the page mapped * outside the softirq. */ /** * iscsi_tcp_segment_init_sg - init indicated scatterlist entry * @segment: the buffer object * @sg: scatterlist * @offset: byte offset into that sg entry * * This function sets up the segment so that subsequent * data is copied to the indicated sg entry, at the given * offset. */ static inline void iscsi_tcp_segment_init_sg(struct iscsi_segment *segment, struct scatterlist *sg, unsigned int offset) { … } /** * iscsi_tcp_segment_map - map the current S/G page * @segment: iscsi_segment * @recv: 1 if called from recv path * * We only need to possibly kmap data if scatter lists are being used, * because the iscsi passthrough and internal IO paths will never use high * mem pages. */ static void iscsi_tcp_segment_map(struct iscsi_segment *segment, int recv) { … } void iscsi_tcp_segment_unmap(struct iscsi_segment *segment) { … } EXPORT_SYMBOL_GPL(…); /* * Splice the digest buffer into the buffer */ static inline void iscsi_tcp_segment_splice_digest(struct iscsi_segment *segment, void *digest) { … } /** * iscsi_tcp_segment_done - check whether the segment is complete * @tcp_conn: iscsi tcp connection * @segment: iscsi segment to check * @recv: set to one of this is called from the recv path * @copied: number of bytes copied * * Check if we're done receiving this segment. If the receive * buffer is full but we expect more data, move on to the * next entry in the scatterlist. * * If the amount of data we received isn't a multiple of 4, * we will transparently receive the pad bytes, too. * * This function must be re-entrant. */ int iscsi_tcp_segment_done(struct iscsi_tcp_conn *tcp_conn, struct iscsi_segment *segment, int recv, unsigned copied) { … } EXPORT_SYMBOL_GPL(…); /** * iscsi_tcp_segment_recv - copy data to segment * @tcp_conn: the iSCSI TCP connection * @segment: the buffer to copy to * @ptr: data pointer * @len: amount of data available * * This function copies up to @len bytes to the * given buffer, and returns the number of bytes * consumed, which can actually be less than @len. * * If hash digest is enabled, the function will update the * hash while copying. * Combining these two operations doesn't buy us a lot (yet), * but in the future we could implement combined copy+crc, * just way we do for network layer checksums. */ static int iscsi_tcp_segment_recv(struct iscsi_tcp_conn *tcp_conn, struct iscsi_segment *segment, const void *ptr, unsigned int len) { … } inline void iscsi_tcp_dgst_header(struct ahash_request *hash, const void *hdr, size_t hdrlen, unsigned char digest[ISCSI_DIGEST_SIZE]) { … } EXPORT_SYMBOL_GPL(…); static inline int iscsi_tcp_dgst_verify(struct iscsi_tcp_conn *tcp_conn, struct iscsi_segment *segment) { … } /* * Helper function to set up segment buffer */ static inline void __iscsi_segment_init(struct iscsi_segment *segment, size_t size, iscsi_segment_done_fn_t *done, struct ahash_request *hash) { … } inline void iscsi_segment_init_linear(struct iscsi_segment *segment, void *data, size_t size, iscsi_segment_done_fn_t *done, struct ahash_request *hash) { … } EXPORT_SYMBOL_GPL(…); inline int iscsi_segment_seek_sg(struct iscsi_segment *segment, struct scatterlist *sg_list, unsigned int sg_count, unsigned int offset, size_t size, iscsi_segment_done_fn_t *done, struct ahash_request *hash) { … } EXPORT_SYMBOL_GPL(…); /** * iscsi_tcp_hdr_recv_prep - prep segment for hdr reception * @tcp_conn: iscsi connection to prep for * * This function always passes NULL for the hash argument, because when this * function is called we do not yet know the final size of the header and want * to delay the digest processing until we know that. */ void iscsi_tcp_hdr_recv_prep(struct iscsi_tcp_conn *tcp_conn) { … } EXPORT_SYMBOL_GPL(…); /* * Handle incoming reply to any other type of command */ static int iscsi_tcp_data_recv_done(struct iscsi_tcp_conn *tcp_conn, struct iscsi_segment *segment) { … } static void iscsi_tcp_data_recv_prep(struct iscsi_tcp_conn *tcp_conn) { … } /** * iscsi_tcp_cleanup_task - free tcp_task resources * @task: iscsi task * * must be called with session back_lock */ void iscsi_tcp_cleanup_task(struct iscsi_task *task) { … } EXPORT_SYMBOL_GPL(…); /** * iscsi_tcp_data_in - SCSI Data-In Response processing * @conn: iscsi connection * @task: scsi command task */ static int iscsi_tcp_data_in(struct iscsi_conn *conn, struct iscsi_task *task) { … } /** * iscsi_tcp_r2t_rsp - iSCSI R2T Response processing * @conn: iscsi connection * @hdr: PDU header */ static int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr) { … } /* * Handle incoming reply to DataIn command */ static int iscsi_tcp_process_data_in(struct iscsi_tcp_conn *tcp_conn, struct iscsi_segment *segment) { … } /** * iscsi_tcp_hdr_dissect - process PDU header * @conn: iSCSI connection * @hdr: PDU header * * This function analyzes the header of the PDU received, * and performs several sanity checks. If the PDU is accompanied * by data, the receive buffer is set up to copy the incoming data * to the correct location. */ static int iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) { … } /** * iscsi_tcp_hdr_recv_done - process PDU header * @tcp_conn: iSCSI TCP connection * @segment: the buffer segment being processed * * This is the callback invoked when the PDU header has * been received. If the header is followed by additional * header segments, we go back for more data. */ static int iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn, struct iscsi_segment *segment) { … } /** * iscsi_tcp_recv_segment_is_hdr - tests if we are reading in a header * @tcp_conn: iscsi tcp conn * * returns non zero if we are currently processing or setup to process * a header. */ inline int iscsi_tcp_recv_segment_is_hdr(struct iscsi_tcp_conn *tcp_conn) { … } EXPORT_SYMBOL_GPL(…); /** * iscsi_tcp_recv_skb - Process skb * @conn: iscsi connection * @skb: network buffer with header and/or data segment * @offset: offset in skb * @offloaded: bool indicating if transfer was offloaded * @status: iscsi TCP status result * * Will return status of transfer in @status. And will return * number of bytes copied. */ int iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb, unsigned int offset, bool offloaded, int *status) { … } EXPORT_SYMBOL_GPL(…); /** * iscsi_tcp_task_init - Initialize iSCSI SCSI_READ or SCSI_WRITE commands * @task: scsi command task */ int iscsi_tcp_task_init(struct iscsi_task *task) { … } EXPORT_SYMBOL_GPL(…); static struct iscsi_r2t_info *iscsi_tcp_get_curr_r2t(struct iscsi_task *task) { … } /** * iscsi_tcp_task_xmit - xmit normal PDU task * @task: iscsi command task * * We're expected to return 0 when everything was transmitted successfully, * -EAGAIN if there's still data in the queue, or != 0 for any other kind * of error. */ int iscsi_tcp_task_xmit(struct iscsi_task *task) { … } EXPORT_SYMBOL_GPL(…); struct iscsi_cls_conn * iscsi_tcp_conn_setup(struct iscsi_cls_session *cls_session, int dd_data_size, uint32_t conn_idx) { … } EXPORT_SYMBOL_GPL(…); void iscsi_tcp_conn_teardown(struct iscsi_cls_conn *cls_conn) { … } EXPORT_SYMBOL_GPL(…); int iscsi_tcp_r2tpool_alloc(struct iscsi_session *session) { … } EXPORT_SYMBOL_GPL(…); void iscsi_tcp_r2tpool_free(struct iscsi_session *session) { … } EXPORT_SYMBOL_GPL(…); int iscsi_tcp_set_max_r2t(struct iscsi_conn *conn, char *buf) { … } EXPORT_SYMBOL_GPL(…); void iscsi_tcp_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats) { … } EXPORT_SYMBOL_GPL(…);