Linux iscsi_sw_tcp_session_create软件iSCSI会话创建
Linux iscsi_sw_tcp_session_create软件iSCSI会话创建iscsi_sw_tcp_session_create是软件iSCSISoftware iSCSI传输层的会话创建函数位于drivers/scsi/iscsi_tcp.c。它实现了struct iscsi_transport模板中的create_session回调负责分配并初始化一个iSCSI会话session及其关联的TCP连接。函数定义cstatic struct iscsi_cls_session *iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep,u16 cmds_max, u16 queue_depth,u32 initial_cmdsn, u32 *mis_len){struct iscsi_cls_session *cls_session;struct iscsi_session *session;struct iscsi_tcp_conn *tcp_conn;struct iscsi_conn *conn;struct iscsi_sw_tcp_conn *sw_tcp_conn;int rc;/* 创建iSCSI会话类 */cls_session iscsi_session_setup(iscsi_sw_tcp_transport,cmds_max, queue_depth,initial_cmdsn, mis_len);if (!cls_session)return ERR_PTR(-ENOMEM);session cls_session-dd_data;session-transport iscsi_sw_tcp_transport;/* 创建连接 */conn iscsi_conn_setup(cls_session, 0, 0);if (!conn) {iscsi_session_teardown(cls_session);return ERR_PTR(-ENOMEM);}/* 分配并初始化传输层连接私有数据 */tcp_conn conn-dd_data;sw_tcp_conn kzalloc(sizeof(*sw_tcp_conn), GFP_KERNEL);if (!sw_tcp_conn) {iscsi_conn_teardown(conn);iscsi_session_teardown(cls_session);return ERR_PTR(-ENOMEM);}tcp_conn-dd_data sw_tcp_conn;tcp_conn-iscsi_conn conn;conn-dd_data tcp_conn;conn-max_recv_dlength ISCSI_DEF_MAX_RECV_SEG_LEN;conn-max_xmit_dlength ISCSI_DEF_MAX_XMIT_SEG_LEN;/* 设置传输操作函数 */tcp_conn-sendpage iscsi_sw_tcp_sendpage;tcp_conn-send_pdu iscsi_sw_tcp_send_pdu;tcp_conn-recv_pdu iscsi_sw_tcp_recv_pdu;tcp_conn-xmit_segment iscsi_sw_tcp_xmit_segment;/* 初始化skb列表 */skb_queue_head_init(sw_tcp_conn-queue);init_completion(sw_tcp_conn-done);return cls_session;}iscsi_session_setup在SCSI中层创建会话结构分配cmd poolcstruct iscsi_cls_session *iscsi_session_setup(struct iscsi_transport *transport,u16 cmds_max, u16 queue_depth,u32 initial_cmdsn, u32 *mis_len){struct iscsi_cls_session *cls_session;struct iscsi_session *session;int cmd_i, err;cls_session kzalloc(sizeof(*cls_session) transport-session_size, GFP_KERNEL);if (!cls_session)return NULL;session cls_session-dd_data;cls_session-transport transport;session-cls_session cls_session;session-state ISCSI_STATE_FREE;session-cmds_max cmds_max;session-queue_depth queue_depth;session-initial_cmdsn initial_cmdsn;session-cmdsn initial_cmdsn;session-max_cmdsn initial_cmdsn (cmds_max - 1);session-exp_cmdsn initial_cmdsn;session-max_r2t ISCSI_DEF_MAX_R2T;session-erl 0;spin_lock_init(session-lock);INIT_LIST_HEAD(session-sess_cmds);INIT_LIST_HEAD(session-cmdpool_wait);INIT_LIST_HEAD(session-cmdpool_free);/* 预分配命令槽 */session-cmdpool iscsi_pool_init(session-cmdpool, cmds_max,NULL, sizeof(struct iscsi_cmd));if (!session-cmdpool) {kfree(cls_session);return NULL;}for (cmd_i 0; cmd_i cmds_max; cmd_i) {struct iscsi_cmd *cmd session-cmdpool[cmd_i];cmd-session session;list_add_tail(cmd-list, session-cmdpool_free);}err iscsi_add_session(cls_session);if (err)goto destroy_pool;return cls_session;destroy_pool:iscsi_pool_free(session-cmdpool);kfree(cls_session);return NULL;}iscsi_conn_setup创建连接并初始化参数cstruct iscsi_conn *iscsi_conn_setup(struct iscsi_cls_session *cls_session,u32 max_recv_dlength, u32 is_leading){struct iscsi_session *session cls_session-dd_data;struct iscsi_conn *conn;struct iscsi_tcp_conn *tcp_conn;conn kzalloc(sizeof(*conn) session-transport-conn_size, GFP_KERNEL);if (!conn)return NULL;conn-session session;conn-max_recv_dlength max_recv_dlength;conn-leading is_leading;conn-persistent_port 0;conn-stop_stage 0;conn-stop_flag 0;conn-tmf_state TMF_INITIAL;conn-ping_timeout 5;conn-recv_timeout 10;conn-recv_timer_stopped 0;tcp_conn conn-dd_data;tcp_conn-iscsi_conn conn;spin_lock_bh(session-lock);list_add_tail(conn-conn_list, session-conns_list);spin_unlock_bh(session-lock);return conn;}软件iSCSI传输层还涉及TCP socket的建立和绑定。连接建立后通过iscsi_sw_tcp_conn_bind绑定到用户态传入的endpointcstatic int iscsi_sw_tcp_conn_bind(struct iscsi_cls_session *cls_session,struct iscsi_cls_conn *cls_conn,struct iscsi_endpoint *ep, int is_leading){struct iscsi_conn *conn cls_conn-dd_data;struct iscsi_tcp_conn *tcp_conn conn-dd_data;struct iscsi_sw_tcp_conn *sw_tcp_conn tcp_conn-dd_data;struct socket *sock;struct sock *sk;int err;sock ep-sock;if (!sock)return -ENOTCONN;sw_tcp_conn-sock sock;/* 设置socket回调 */write_lock_bh(sock-sk-sk_callback_lock);sk sock-sk;sw_tcp_conn-old_state_change sk-sk_state_change;sw_tcp_conn-old_data_ready sk-sk_data_ready;sw_tcp_conn-old_write_space sk-sk_write_space;sk-sk_user_data conn;sk-sk_state_change iscsi_sw_tcp_state_change;sk-sk_data_ready iscsi_sw_tcp_data_ready;sk-sk_write_space iscsi_sw_tcp_write_space;write_unlock_bh(sock-sk-sk_callback_lock);conn-sock sock;session-send_pdu iscsi_sw_tcp_send_pdu;session-recv_pdu iscsi_sw_tcp_recv_pdu;return 0;}创建完成后会话进入ISCSI_STATE_LOGGED_IN状态通过iscsi_conn_start启动连接并开始处理SCSI命令。整个创建流程从用户态通过ioctl或sysfs触发经过SCSI中层、iSCSI传输层、TCP socket层的层层初始化最终形成一个可传输SCSI命令的完整会话通道。