📄 iscsi_tcp.c
字号:
crypto_free_hash(tcp_conn->rx_hash.tfm); kfree(tcp_conn);}static voidiscsi_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag){ struct iscsi_conn *conn = cls_conn->dd_data; struct iscsi_tcp_conn *tcp_conn = conn->dd_data; iscsi_conn_stop(cls_conn, flag); iscsi_tcp_release_conn(conn); tcp_conn->hdr_size = sizeof(struct iscsi_hdr);}static int iscsi_tcp_get_addr(struct iscsi_conn *conn, struct socket *sock, char *buf, int *port, int (*getname)(struct socket *, struct sockaddr *, int *addrlen)){ struct sockaddr_storage *addr; struct sockaddr_in6 *sin6; struct sockaddr_in *sin; int rc = 0, len; addr = kmalloc(sizeof(*addr), GFP_KERNEL); if (!addr) return -ENOMEM; if (getname(sock, (struct sockaddr *) addr, &len)) { rc = -ENODEV; goto free_addr; } switch (addr->ss_family) { case AF_INET: sin = (struct sockaddr_in *)addr; spin_lock_bh(&conn->session->lock); sprintf(buf, NIPQUAD_FMT, NIPQUAD(sin->sin_addr.s_addr)); *port = be16_to_cpu(sin->sin_port); spin_unlock_bh(&conn->session->lock); break; case AF_INET6: sin6 = (struct sockaddr_in6 *)addr; spin_lock_bh(&conn->session->lock); sprintf(buf, NIP6_FMT, NIP6(sin6->sin6_addr)); *port = be16_to_cpu(sin6->sin6_port); spin_unlock_bh(&conn->session->lock); break; }free_addr: kfree(addr); return rc;}static intiscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session, struct iscsi_cls_conn *cls_conn, uint64_t transport_eph, int is_leading){ struct iscsi_conn *conn = cls_conn->dd_data; struct iscsi_tcp_conn *tcp_conn = conn->dd_data; struct sock *sk; struct socket *sock; int err; /* lookup for existing socket */ sock = sockfd_lookup((int)transport_eph, &err); if (!sock) { printk(KERN_ERR "iscsi_tcp: sockfd_lookup failed %d\n", err); return -EEXIST; } /* * copy these values now because if we drop the session * userspace may still want to query the values since we will * be using them for the reconnect */ err = iscsi_tcp_get_addr(conn, sock, conn->portal_address, &conn->portal_port, kernel_getpeername); if (err) goto free_socket; err = iscsi_tcp_get_addr(conn, sock, conn->local_address, &conn->local_port, kernel_getsockname); if (err) goto free_socket; err = iscsi_conn_bind(cls_session, cls_conn, is_leading); if (err) goto free_socket; /* bind iSCSI connection and socket */ tcp_conn->sock = sock; /* setup Socket parameters */ sk = sock->sk; sk->sk_reuse = 1; sk->sk_sndtimeo = 15 * HZ; /* FIXME: make it configurable */ sk->sk_allocation = GFP_ATOMIC; /* FIXME: disable Nagle's algorithm */ /* * Intercept TCP callbacks for sendfile like receive * processing. */ conn->recv_lock = &sk->sk_callback_lock; iscsi_conn_set_callbacks(conn); tcp_conn->sendpage = tcp_conn->sock->ops->sendpage; /* * set receive state machine into initial state */ tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER; return 0;free_socket: sockfd_put(sock); return err;}/* called with host lock */static voidiscsi_tcp_mgmt_init(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask){ struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data; tcp_mtask->xmstate = 1 << XMSTATE_BIT_IMM_HDR_INIT;}static intiscsi_r2tpool_alloc(struct iscsi_session *session){ int i; int cmd_i; /* * initialize per-task: R2T pool and xmit queue */ for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) { struct iscsi_cmd_task *ctask = session->cmds[cmd_i]; struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; /* * pre-allocated x4 as much r2ts to handle race when * target acks DataOut faster than we data_xmit() queues * could replenish r2tqueue. */ /* R2T pool */ if (iscsi_pool_init(&tcp_ctask->r2tpool, session->max_r2t * 4, (void***)&tcp_ctask->r2ts, sizeof(struct iscsi_r2t_info))) { goto r2t_alloc_fail; } /* R2T xmit queue */ tcp_ctask->r2tqueue = kfifo_alloc( session->max_r2t * 4 * sizeof(void*), GFP_KERNEL, NULL); if (tcp_ctask->r2tqueue == ERR_PTR(-ENOMEM)) { iscsi_pool_free(&tcp_ctask->r2tpool, (void**)tcp_ctask->r2ts); goto r2t_alloc_fail; } } return 0;r2t_alloc_fail: for (i = 0; i < cmd_i; i++) { struct iscsi_cmd_task *ctask = session->cmds[i]; struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; kfifo_free(tcp_ctask->r2tqueue); iscsi_pool_free(&tcp_ctask->r2tpool, (void**)tcp_ctask->r2ts); } return -ENOMEM;}static voidiscsi_r2tpool_free(struct iscsi_session *session){ int i; for (i = 0; i < session->cmds_max; i++) { struct iscsi_cmd_task *ctask = session->cmds[i]; struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; kfifo_free(tcp_ctask->r2tqueue); iscsi_pool_free(&tcp_ctask->r2tpool, (void**)tcp_ctask->r2ts); }}static intiscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param, char *buf, int buflen){ struct iscsi_conn *conn = cls_conn->dd_data; struct iscsi_session *session = conn->session; struct iscsi_tcp_conn *tcp_conn = conn->dd_data; int value; switch(param) { case ISCSI_PARAM_HDRDGST_EN: iscsi_set_param(cls_conn, param, buf, buflen); tcp_conn->hdr_size = sizeof(struct iscsi_hdr); if (conn->hdrdgst_en) tcp_conn->hdr_size += sizeof(__u32); break; case ISCSI_PARAM_DATADGST_EN: iscsi_set_param(cls_conn, param, buf, buflen); tcp_conn->sendpage = conn->datadgst_en ? sock_no_sendpage : tcp_conn->sock->ops->sendpage; break; case ISCSI_PARAM_MAX_R2T: sscanf(buf, "%d", &value); if (session->max_r2t == roundup_pow_of_two(value)) break; iscsi_r2tpool_free(session); iscsi_set_param(cls_conn, param, buf, buflen); if (session->max_r2t & (session->max_r2t - 1)) session->max_r2t = roundup_pow_of_two(session->max_r2t); if (iscsi_r2tpool_alloc(session)) return -ENOMEM; break; default: return iscsi_set_param(cls_conn, param, buf, buflen); } return 0;}static intiscsi_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param, char *buf){ struct iscsi_conn *conn = cls_conn->dd_data; int len; switch(param) { case ISCSI_PARAM_CONN_PORT: spin_lock_bh(&conn->session->lock); len = sprintf(buf, "%hu\n", conn->portal_port); spin_unlock_bh(&conn->session->lock); break; case ISCSI_PARAM_CONN_ADDRESS: spin_lock_bh(&conn->session->lock); len = sprintf(buf, "%s\n", conn->portal_address); spin_unlock_bh(&conn->session->lock); break; default: return iscsi_conn_get_param(cls_conn, param, buf); } return len;}static intiscsi_tcp_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param, char *buf){ struct iscsi_session *session = iscsi_hostdata(shost->hostdata); int len; switch (param) { case ISCSI_HOST_PARAM_IPADDRESS: spin_lock_bh(&session->lock); if (!session->leadconn) len = -ENODEV; else len = sprintf(buf, "%s\n", session->leadconn->local_address); spin_unlock_bh(&session->lock); break; default: return iscsi_host_get_param(shost, param, buf); } return len;}static voidiscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats){ struct iscsi_conn *conn = cls_conn->dd_data; struct iscsi_tcp_conn *tcp_conn = conn->dd_data; stats->txdata_octets = conn->txdata_octets; stats->rxdata_octets = conn->rxdata_octets; stats->scsicmd_pdus = conn->scsicmd_pdus_cnt; stats->dataout_pdus = conn->dataout_pdus_cnt; stats->scsirsp_pdus = conn->scsirsp_pdus_cnt; stats->datain_pdus = conn->datain_pdus_cnt; stats->r2t_pdus = conn->r2t_pdus_cnt; stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt; stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt; stats->custom_length = 3; strcpy(stats->custom[0].desc, "tx_sendpage_failures"); stats->custom[0].value = tcp_conn->sendpage_failures_cnt; strcpy(stats->custom[1].desc, "rx_discontiguous_hdr"); stats->custom[1].value = tcp_conn->discontiguous_hdr_cnt; strcpy(stats->custom[2].desc, "eh_abort_cnt"); stats->custom[2].value = conn->eh_abort_cnt;}static struct iscsi_cls_session *iscsi_tcp_session_create(struct iscsi_transport *iscsit, struct scsi_transport_template *scsit, uint16_t cmds_max, uint16_t qdepth, uint32_t initial_cmdsn, uint32_t *hostno){ struct iscsi_cls_session *cls_session; struct iscsi_session *session; uint32_t hn; int cmd_i; cls_session = iscsi_session_setup(iscsit, scsit, cmds_max, qdepth, sizeof(struct iscsi_tcp_cmd_task), sizeof(struct iscsi_tcp_mgmt_task), initial_cmdsn, &hn); if (!cls_session) return NULL; *hostno = hn; session = class_to_transport_session(cls_session); for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) { struct iscsi_cmd_task *ctask = session->cmds[cmd_i]; struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; ctask->hdr = &tcp_ctask->hdr; } for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++) { struct iscsi_mgmt_task *mtask = session->mgmt_cmds[cmd_i]; struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data; mtask->hdr = &tcp_mtask->hdr; } if (iscsi_r2tpool_alloc(class_to_transport_session(cls_session))) goto r2tpool_alloc_fail; return cls_session;r2tpool_alloc_fail: iscsi_session_teardown(cls_session); return NULL;}static void iscsi_tcp_session_destroy(struct iscsi_cls_session *cls_session){ iscsi_r2tpool_free(class_to_transport_session(cls_session)); iscsi_session_teardown(cls_session);}static int iscsi_tcp_slave_configure(struct scsi_device *sdev){ blk_queue_bounce_limit(sdev->request_queue, BLK_BOUNCE_ANY); blk_queue_dma_alignment(sdev->request_queue, 0); return 0;}static struct scsi_host_template iscsi_sht = { .module = THIS_MODULE, .name = "iSCSI Initiator over TCP/IP", .queuecommand = iscsi_queuecommand, .change_queue_depth = iscsi_change_queue_depth, .can_queue = ISCSI_DEF_XMIT_CMDS_MAX - 1, .sg_tablesize = ISCSI_SG_TABLESIZE, .max_sectors = 0xFFFF, .cmd_per_lun = ISCSI_DEF_CMD_PER_LUN, .eh_abort_handler = iscsi_eh_abort, .eh_host_reset_handler = iscsi_eh_host_reset, .use_clustering = DISABLE_CLUSTERING, .slave_configure = iscsi_tcp_slave_configure, .proc_name = "iscsi_tcp", .this_id = -1,};static struct iscsi_transport iscsi_tcp_transport = { .owner = THIS_MODULE, .name = "tcp", .caps = CAP_RECOVERY_L0 | CAP_MULTI_R2T | CAP_HDRDGST | CAP_DATADGST, .param_mask = ISCSI_MAX_RECV_DLENGTH | ISCSI_MAX_XMIT_DLENGTH | ISCSI_HDRDGST_EN | ISCSI_DATADGST_EN | ISCSI_INITIAL_R2T_EN | ISCSI_MAX_R2T | ISCSI_IMM_DATA_EN | ISCSI_FIRST_BURST | ISCSI_MAX_BURST | ISCSI_PDU_INORDER_EN | ISCSI_DATASEQ_INORDER_EN | ISCSI_ERL | ISCSI_CONN_PORT | ISCSI_CONN_ADDRESS | ISCSI_EXP_STATSN | ISCSI_PERSISTENT_PORT | ISCSI_PERSISTENT_ADDRESS | ISCSI_TARGET_NAME | ISCSI_TPGT | ISCSI_USERNAME | ISCSI_PASSWORD | ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN, .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS | ISCSI_HOST_INITIATOR_NAME | ISCSI_HOST_NETDEV_NAME, .host_template = &iscsi_sht, .conndata_size = sizeof(struct iscsi_conn), .max_conn = 1, .max_cmd_len = ISCSI_TCP_MAX_CMD_LEN, /* session management */ .create_session = iscsi_tcp_session_create, .destroy_session = iscsi_tcp_session_destroy, /* connection management */ .create_conn = iscsi_tcp_conn_create, .bind_conn = iscsi_tcp_conn_bind, .destroy_conn = iscsi_tcp_conn_destroy, .set_param = iscsi_conn_set_param, .get_conn_param = iscsi_tcp_conn_get_param, .get_session_param = iscsi_session_get_param, .start_conn = iscsi_conn_start, .stop_conn = iscsi_tcp_conn_stop, /* iscsi host params */ .get_host_param = iscsi_tcp_host_get_param, .set_host_param = iscsi_host_set_param, /* IO */ .send_pdu = iscsi_conn_send_pdu, .get_stats = iscsi_conn_get_stats, .init_cmd_task = iscsi_tcp_cmd_init, .init_mgmt_task = iscsi_tcp_mgmt_init, .xmit_cmd_task = iscsi_tcp_ctask_xmit, .xmit_mgmt_task = iscsi_tcp_mtask_xmit, .cleanup_cmd_task = iscsi_tcp_cleanup_ctask, /* recovery */ .session_recovery_timedout = iscsi_session_recovery_timedout,};static int __initiscsi_tcp_init(void){ if (iscsi_max_lun < 1) { printk(KERN_ERR "iscsi_tcp: Invalid max_lun value of %u\n", iscsi_max_lun); return -EINVAL; } iscsi_tcp_transport.max_lun = iscsi_max_lun; if (!iscsi_register_transport(&iscsi_tcp_transport)) return -ENODEV; return 0;}static void __exitiscsi_tcp_exit(void){ iscsi_unregister_transport(&iscsi_tcp_transport);}module_init(iscsi_tcp_init);module_exit(iscsi_tcp_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -