diff --git a/iscsi-scst/doc/iscsi-scst-howto.txt b/iscsi-scst/doc/iscsi-scst-howto.txt index 886c04d97..0243ffb91 100644 --- a/iscsi-scst/doc/iscsi-scst-howto.txt +++ b/iscsi-scst/doc/iscsi-scst-howto.txt @@ -262,21 +262,21 @@ Show the configured parameters for a specific target: # iscsi-scst-adm --op show --tid=1 --sid=0 InitialR2T=No -ImmediateData= -MaxConnections=0 -MaxRecvDataSegmentLength=4269849632 -MaxXmitDataSegmentLength=10933 -MaxBurstLength=4294967295 -FirstBurstLength=32767 -DefaultTime2Wait=4203042 -DefaultTime2Retain=5 -MaxOutstandingR2T=0 -DataPDUInOrder=No +ImmediateData=Yes +MaxConnections=1 +MaxRecvDataSegmentLength=2097152 +MaxXmitDataSegmentLength=131072 +MaxBurstLength=2097152 +FirstBurstLength=262144 +DefaultTime2Wait=2 +DefaultTime2Retain=0 +MaxOutstandingR2T=1 +DataPDUInOrder=Yes DataSequenceInOrder=Yes ErrorRecoveryLevel=0 -HeaderDigest= -DataDigest=CRC32C -OFMarker= +HeaderDigest=None +DataDigest=None +OFMarker=No IFMarker=No OFMarkInt=Reject IFMarkInt=Reject diff --git a/iscsi-scst/include/iscsi_scst.h b/iscsi-scst/include/iscsi_scst.h index 9c7bc3cd9..086c526bd 100644 --- a/iscsi-scst/include/iscsi_scst.h +++ b/iscsi-scst/include/iscsi_scst.h @@ -34,12 +34,12 @@ #define aligned_u64 uint64_t __attribute__((aligned(8))) #endif -struct target_info { +struct iscsi_kern_target_info { u32 tid; char name[ISCSI_NAME_LEN]; }; -struct session_info { +struct iscsi_kern_session_info { u32 tid; aligned_u64 sid; @@ -52,7 +52,7 @@ struct session_info { #define DIGEST_NONE (1 << 0) #define DIGEST_CRC32C (1 << 1) -struct conn_info { +struct iscsi_kern_conn_info { u32 tid; aligned_u64 sid; @@ -97,7 +97,7 @@ enum { key_target, }; -struct iscsi_param_info { +struct iscsi_kern_param_info { u32 tid; aligned_u64 sid; @@ -108,18 +108,18 @@ struct iscsi_param_info { s32 target_param[target_key_last]; }; -enum iscsi_event_state { +enum iscsi_kern_event_state { E_CONN_CLOSE, }; -struct iscsi_event { +struct iscsi_kern_event { u32 tid; aligned_u64 sid; u32 cid; u32 state; }; -struct iscsi_register_info { +struct iscsi_kern_register_info { aligned_u64 version; }; @@ -130,18 +130,16 @@ struct iscsi_register_info { #define NETLINK_ISCSI_SCST 25 /* On success returns MAX_DATA_SEG_LEN */ -#define REGISTER_USERD _IOW('s', 0, struct iscsi_register_info) +#define REGISTER_USERD _IOW('s', 0, struct iscsi_kern_register_info) -#define ADD_TARGET _IOW('s', 1, struct target_info) -#define DEL_TARGET _IOW('s', 2, struct target_info) -#define ADD_SESSION _IOW('s', 3, struct session_info) -#define DEL_SESSION _IOW('s', 4, struct session_info) -#define GET_SESSION_INFO _IOWR('s', 5, struct session_info) -#define ADD_CONN _IOW('s', 6, struct conn_info) -#define DEL_CONN _IOW('s', 7, struct conn_info) -#define GET_CONN_INFO _IOWR('s', 8, struct conn_info) -#define ISCSI_PARAM_SET _IOW('s', 9, struct iscsi_param_info) -#define ISCSI_PARAM_GET _IOWR('s', 10, struct iscsi_param_info) +#define ADD_TARGET _IOW('s', 1, struct iscsi_kern_target_info) +#define DEL_TARGET _IOW('s', 2, struct iscsi_kern_target_info) +#define ADD_SESSION _IOW('s', 3, struct iscsi_kern_session_info) +#define DEL_SESSION _IOW('s', 4, struct iscsi_kern_session_info) +#define ADD_CONN _IOW('s', 5, struct iscsi_kern_conn_info) +#define DEL_CONN _IOW('s', 6, struct iscsi_kern_conn_info) +#define ISCSI_PARAM_SET _IOW('s', 7, struct iscsi_kern_param_info) +#define ISCSI_PARAM_GET _IOWR('s', 8, struct iscsi_kern_param_info) static inline int iscsi_is_key_declarative(int key) { diff --git a/iscsi-scst/kernel/config.c b/iscsi-scst/kernel/config.c index 0831c6957..5bdd98fa6 100644 --- a/iscsi-scst/kernel/config.c +++ b/iscsi-scst/kernel/config.c @@ -229,39 +229,12 @@ err: goto out; } -/* target_mutex supposed to be locked */ -static int get_conn_info(struct iscsi_target *target, void __user *ptr) -{ - int err; - struct iscsi_session *session; - struct conn_info info; - struct iscsi_conn *conn; - - err = copy_from_user(&info, ptr, sizeof(info)); - if (err < 0) - return err; - - session = session_lookup(target, info.sid); - if (!session) - return -ENOENT; - conn = conn_lookup(session, info.cid); - - info.cid = conn->cid; - info.stat_sn = conn->stat_sn; - info.exp_stat_sn = conn->exp_stat_sn; - - if (copy_to_user(ptr, &info, sizeof(info))) - return -EFAULT; - - return 0; -} - /* target_mutex supposed to be locked */ static int add_conn(struct iscsi_target *target, void __user *ptr) { int err; struct iscsi_session *session; - struct conn_info info; + struct iscsi_kern_conn_info info; err = copy_from_user(&info, ptr, sizeof(info)); if (err < 0) @@ -279,7 +252,7 @@ static int del_conn(struct iscsi_target *target, void __user *ptr) { int err; struct iscsi_session *session; - struct conn_info info; + struct iscsi_kern_conn_info info; err = copy_from_user(&info, ptr, sizeof(info)); if (err < 0) @@ -292,35 +265,11 @@ static int del_conn(struct iscsi_target *target, void __user *ptr) return conn_del(session, &info); } -/* target_mutex supposed to be locked */ -static int get_session_info(struct iscsi_target *target, void __user *ptr) -{ - int err; - struct iscsi_session *session; - struct session_info info; - - err = copy_from_user(&info, ptr, sizeof(info)); - if (err < 0) - return err; - - session = session_lookup(target, info.sid); - - if (!session) - return -ENOENT; - - info.exp_cmd_sn = session->exp_cmd_sn; - - if (copy_to_user(ptr, &info, sizeof(info))) - return -EFAULT; - - return 0; -} - /* target_mutex supposed to be locked */ static int add_session(struct iscsi_target *target, void __user *ptr) { int err; - struct session_info info; + struct iscsi_kern_session_info info; err = copy_from_user(&info, ptr, sizeof(info)); if (err < 0) @@ -336,7 +285,7 @@ static int add_session(struct iscsi_target *target, void __user *ptr) static int del_session(struct iscsi_target *target, void __user *ptr) { int err; - struct session_info info; + struct iscsi_kern_session_info info; err = copy_from_user(&info, ptr, sizeof(info)); if (err < 0) @@ -350,7 +299,7 @@ static int iscsi_param_config(struct iscsi_target *target, void __user *ptr, int set) { int err; - struct iscsi_param_info info; + struct iscsi_kern_param_info info; err = copy_from_user(&info, ptr, sizeof(info)); if (err < 0) @@ -371,7 +320,7 @@ out: static int add_target(void __user *ptr) { int err; - struct target_info info; + struct iscsi_kern_target_info info; err = copy_from_user(&info, ptr, sizeof(info)); if (err < 0) @@ -386,7 +335,7 @@ static int add_target(void __user *ptr) static int iscsi_check_version(void __user *arg) { - struct iscsi_register_info reg; + struct iscsi_kern_register_info reg; char ver[sizeof(ISCSI_SCST_INTERFACE_VERSION)+1]; int res; @@ -428,12 +377,10 @@ static long ioctl(struct file *file, unsigned int cmd, unsigned long arg) case DEL_TARGET: case ADD_SESSION: case DEL_SESSION: - case GET_SESSION_INFO: case ISCSI_PARAM_SET: case ISCSI_PARAM_GET: case ADD_CONN: case DEL_CONN: - case GET_CONN_INFO: break; case REGISTER_USERD: @@ -491,10 +438,6 @@ static long ioctl(struct file *file, unsigned int cmd, unsigned long arg) err = del_session(target, (void __user *) arg); break; - case GET_SESSION_INFO: - err = get_session_info(target, (void __user *) arg); - break; - case ISCSI_PARAM_SET: err = iscsi_param_config(target, (void __user *) arg, 1); break; @@ -511,10 +454,6 @@ static long ioctl(struct file *file, unsigned int cmd, unsigned long arg) err = del_conn(target, (void __user *) arg); break; - case GET_CONN_INFO: - err = get_conn_info(target, (void __user *) arg); - break; - default: sBUG(); break; @@ -555,9 +494,9 @@ static void iscsi_dump_char(int ch) text[i] = ' '; i++; if ((i % 16) == 0) - printk(LOG_FLAG " | %.16s |\n", text); + printk(" | %.16s |\n", text); else if ((i % 4) == 0) - printk(LOG_FLAG " |"); + printk(" |"); } i = 0; return; @@ -567,10 +506,10 @@ static void iscsi_dump_char(int ch) printk(LOG_FLAG " %02x", ch); i++; if ((i % 16) == 0) { - printk(LOG_FLAG " | %.16s |\n", text); + printk(" | %.16s |\n", text); i = 0; } else if ((i % 4) == 0) - printk(LOG_FLAG " |"); + printk(" |"); } void iscsi_dump_pdu(struct iscsi_pdu *pdu) diff --git a/iscsi-scst/kernel/conn.c b/iscsi-scst/kernel/conn.c index 55e45d3b2..42bd843ef 100644 --- a/iscsi-scst/kernel/conn.c +++ b/iscsi-scst/kernel/conn.c @@ -107,7 +107,12 @@ struct iscsi_conn *conn_lookup(struct iscsi_session *session, u16 cid) { struct iscsi_conn *conn; - list_for_each_entry(conn, &session->conn_list, conn_list_entry) { + /* + * We need to find the latest conn to correctly handle + * multi-reinstatements + */ + list_for_each_entry_reverse(conn, &session->conn_list, + conn_list_entry) { if (conn->cid == cid) return conn; } @@ -118,6 +123,8 @@ static void iscsi_make_conn_rd_active(struct iscsi_conn *conn) { TRACE_ENTRY(); + EXTRACHECKS_BUG_ON(conn->conn_reinstating); + spin_lock_bh(&iscsi_rd_lock); TRACE_DBG("conn %p, rd_state %x, rd_data_ready %d", conn, @@ -168,7 +175,8 @@ void __mark_conn_closed(struct iscsi_conn *conn, int flags) conn->deleting = 1; spin_unlock_bh(&iscsi_rd_lock); - iscsi_make_conn_rd_active(conn); + if (!conn->conn_reinstating) + iscsi_make_conn_rd_active(conn); } void mark_conn_closed(struct iscsi_conn *conn) @@ -184,9 +192,11 @@ static void iscsi_state_change(struct sock *sk) if (unlikely(sk->sk_state != TCP_ESTABLISHED)) { if (!conn->closing) { - PRINT_ERROR("Connection with initiator %s (%p) " + PRINT_ERROR("Connection with initiator %s " "unexpectedly closed!", - conn->session->initiator_name, conn); + conn->session->initiator_name); + TRACE_MGMT_DBG("conn %p, sk state %d", conn, + sk->sk_state); __mark_conn_closed(conn, 0); } } else @@ -275,13 +285,41 @@ static void conn_rsp_timer_fn(unsigned long arg) return; } +void __iscsi_socket_bind(struct iscsi_conn *conn) +{ + TRACE_MGMT_DBG("Enabling conn %p", conn); + + /* Catch double bind */ + sBUG_ON(conn->sock->sk->sk_state_change == iscsi_state_change); + + /* Let's reset this flag in one place */ + conn->conn_reinstating = 0; + + write_lock_bh(&conn->sock->sk->sk_callback_lock); + + conn->old_state_change = conn->sock->sk->sk_state_change; + conn->sock->sk->sk_state_change = iscsi_state_change; + + conn->old_data_ready = conn->sock->sk->sk_data_ready; + conn->sock->sk->sk_data_ready = iscsi_data_ready; + + conn->old_write_space = conn->sock->sk->sk_write_space; + conn->sock->sk->sk_write_space = iscsi_write_space_ready; + + write_unlock_bh(&conn->sock->sk->sk_callback_lock); + + iscsi_make_conn_rd_active(conn); + + return; +} + /* - * Note: the code belows passes a kernel space pointer (&opt) to setsockopt() + * Note: the code below passes a kernel space pointer (&opt) to setsockopt() * while the declaration of setsockopt specifies that it expects a user space * pointer. This seems to work fine, and this approach is also used in some * other parts of the Linux kernel (see e.g. fs/ocfs2/cluster/tcp.c). */ -static int iscsi_socket_bind(struct iscsi_conn *conn) +static int iscsi_socket_bind(struct iscsi_conn *conn, bool reinstating) { int res = 0; int opt = 1; @@ -304,25 +342,23 @@ static int iscsi_socket_bind(struct iscsi_conn *conn) #endif conn->sock->sk->sk_user_data = conn; - write_lock_bh(&conn->sock->sk->sk_callback_lock); - - conn->old_state_change = conn->sock->sk->sk_state_change; - conn->sock->sk->sk_state_change = iscsi_state_change; - - conn->old_data_ready = conn->sock->sk->sk_data_ready; - conn->sock->sk->sk_data_ready = iscsi_data_ready; - - conn->old_write_space = conn->sock->sk->sk_write_space; - conn->sock->sk->sk_write_space = iscsi_write_space_ready; - - write_unlock_bh(&conn->sock->sk->sk_callback_lock); - oldfs = get_fs(); set_fs(get_ds()); conn->sock->ops->setsockopt(conn->sock, SOL_TCP, TCP_NODELAY, (void __force __user *)&opt, sizeof(opt)); set_fs(oldfs); + if (!reinstating) { + /* + * We will delay full conn serving until all commands in + * replacing connections are done to prevent them from + * spoil our data by writing to them too late. + */ + __iscsi_socket_bind(conn); + } else + TRACE_MGMT_DBG("conn %p is reinstating, delaying enabling it", + conn); + out: return res; } @@ -341,6 +377,19 @@ int conn_free(struct iscsi_conn *conn) sBUG_ON(!list_empty(&conn->cmd_list)); sBUG_ON(!list_empty(&conn->write_list)); sBUG_ON(!list_empty(&conn->written_list)); + sBUG_ON(conn->conn_reinst_successor != NULL); + + if (conn->conn_reinstating) { + struct iscsi_conn *c; + TRACE_MGMT_DBG("Freeing being reinstated conn %p", conn); + list_for_each_entry(c, &conn->session->conn_list, + conn_list_entry) { + if (c->conn_reinst_successor == conn) { + c->conn_reinst_successor = NULL; + break; + } + } + } list_del(&conn->conn_list_entry); @@ -357,11 +406,14 @@ int conn_free(struct iscsi_conn *conn) /* target_mutex supposed to be locked */ static int iscsi_conn_alloc(struct iscsi_session *session, - struct conn_info *info) + struct iscsi_kern_conn_info *info, bool reinstating, + struct iscsi_conn **new_conn) { struct iscsi_conn *conn; int res = 0; + reinstating |= session->sess_reinstating; + conn = kzalloc(sizeof(*conn), GFP_KERNEL); if (!conn) { res = -ENOMEM; @@ -380,6 +432,7 @@ static int iscsi_conn_alloc(struct iscsi_session *session, atomic_set(&conn->conn_ref_cnt, 0); conn->session = session; + conn->conn_reinstating = reinstating; conn->cid = info->cid; conn->stat_sn = info->stat_sn; conn->exp_stat_sn = info->exp_stat_sn; @@ -399,14 +452,17 @@ static int iscsi_conn_alloc(struct iscsi_session *session, INIT_LIST_HEAD(&conn->write_list); INIT_LIST_HEAD(&conn->written_list); setup_timer(&conn->rsp_timer, conn_rsp_timer_fn, (unsigned long)conn); + init_completion(&conn->ready_to_free); conn->file = fget(info->fd); - res = iscsi_socket_bind(conn); + res = iscsi_socket_bind(conn, reinstating); if (res != 0) goto out_err_free2; - list_add(&conn->conn_list_entry, &session->conn_list); + list_add_tail(&conn->conn_list_entry, &session->conn_list); + + *new_conn = conn; out: return res; @@ -425,20 +481,38 @@ out_err: } /* target_mutex supposed to be locked */ -int conn_add(struct iscsi_session *session, struct conn_info *info) +int conn_add(struct iscsi_session *session, struct iscsi_kern_conn_info *info) { - struct iscsi_conn *conn; - int err = -EEXIST; + struct iscsi_conn *conn, *new_conn; + int err; + bool reinstatement = false; conn = conn_lookup(session, info->cid); - if (conn) - return err; + if (conn != NULL) { + /* conn reinstatement */ + reinstatement = true; + } else if (!list_empty(&session->conn_list)) { + err = -EEXIST; + goto out; + } - return iscsi_conn_alloc(session, info); + err = iscsi_conn_alloc(session, info, reinstatement, &new_conn); + if (err != 0) + goto out; + + if (reinstatement) { + TRACE_MGMT_DBG("Reinstating conn %p", conn); + conn->conn_reinst_successor = new_conn; + new_conn->conn_reinstating = 1; + __mark_conn_closed(conn, 0); + } + +out: + return err; } /* target_mutex supposed to be locked */ -int conn_del(struct iscsi_session *session, struct conn_info *info) +int conn_del(struct iscsi_session *session, struct iscsi_kern_conn_info *info) { struct iscsi_conn *conn; int err = -EEXIST; diff --git a/iscsi-scst/kernel/event.c b/iscsi-scst/kernel/event.c index 45675e0e4..4b332431f 100644 --- a/iscsi-scst/kernel/event.c +++ b/iscsi-scst/kernel/event.c @@ -107,14 +107,14 @@ static int notify(void *data, int len, gfp_t gfp_mask) int event_send(u32 tid, u64 sid, u32 cid, u32 state, int atomic) { int err; - struct iscsi_event event; + struct iscsi_kern_event event; event.tid = tid; event.sid = sid; event.cid = cid; event.state = state; - err = notify(&event, NLMSG_SPACE(sizeof(struct iscsi_event)), 0); + err = notify(&event, NLMSG_SPACE(sizeof(struct iscsi_kern_event)), 0); return err; } diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index affa35985..3e1fffd07 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -572,7 +572,7 @@ static void iscsi_cmnd_init_write(struct iscsi_cmnd *rsp, int flags) list_empty(&rsp->rsp_cmd_list), rsp->hashed); sBUG(); } - list_add(&rsp->write_list_entry, &head); + list_add_tail(&rsp->write_list_entry, &head); iscsi_cmnds_init_write(&head, flags); return; } @@ -1638,7 +1638,7 @@ static void __cmnd_abort(struct iscsi_cmnd *cmnd) return; } -/* Must be called from the read thread */ +/* Must be called from the read or conn close thread */ static int cmnd_abort(struct iscsi_cmnd *req) { struct iscsi_session *session = req->conn->session; @@ -1719,7 +1719,7 @@ out_put: goto out; } -/* Must be called from the read thread */ +/* Must be called from the read or conn close thread */ static int target_abort(struct iscsi_cmnd *req, int all) { struct iscsi_target *target = req->conn->session->target; @@ -1753,7 +1753,7 @@ static int target_abort(struct iscsi_cmnd *req, int all) return 0; } -/* Must be called from the read thread */ +/* Must be called from the read or conn close thread */ static void task_set_abort(struct iscsi_cmnd *req) { struct iscsi_session *session = req->conn->session; @@ -1785,7 +1785,7 @@ static void task_set_abort(struct iscsi_cmnd *req) return; } -/* Must be called from the read thread */ +/* Must be called from the read or conn close thread */ void conn_abort(struct iscsi_conn *conn) { struct iscsi_cmnd *cmnd; @@ -2237,7 +2237,7 @@ void cmnd_tx_end(struct iscsi_cmnd *cmnd) "initiator's %s request", cmnd->conn->session->target->tid, conn->session->initiator_name); - target_del_all_sess(cmnd->conn->session->target, false); + target_del_all_sess(cmnd->conn->session->target, 0); } else { PRINT_INFO("Closing connection at initiator's %s " "request", conn->session->initiator_name); @@ -2300,6 +2300,8 @@ static void iscsi_session_push_cmnd(struct iscsi_cmnd *cmnd) iscsi_cmnd_exec(cmnd); + spin_lock(&session->sn_lock); + if (list_empty(&session->pending_list)) break; cmnd = list_entry(session->pending_list.next, @@ -2313,8 +2315,6 @@ static void iscsi_session_push_cmnd(struct iscsi_cmnd *cmnd) TRACE_DBG("Processing pending cmd %p (cmd_sn %u)", cmnd, cmd_sn); - - spin_lock(&session->sn_lock); } } else { int drop = 0; @@ -2377,6 +2377,7 @@ static void iscsi_session_push_cmnd(struct iscsi_cmnd *cmnd) PRINT_ERROR("%s", "Unable to create TM clone"); } + spin_lock(&session->sn_lock); list_for_each(entry, &session->pending_list) { struct iscsi_cmnd *tmp = list_entry(entry, struct iscsi_cmnd, @@ -2384,10 +2385,11 @@ static void iscsi_session_push_cmnd(struct iscsi_cmnd *cmnd) if (before(cmd_sn, tmp->pdu.bhs.sn)) break; } - list_add_tail(&cmnd->pending_list_entry, entry); cmnd->pending = 1; } + + spin_unlock(&session->sn_lock); out: return; } @@ -2888,19 +2890,25 @@ static inline int iscsi_get_mgmt_response(int status) static void iscsi_task_mgmt_fn_done(struct scst_mgmt_cmd *scst_mcmd) { + int fn = scst_mgmt_cmd_get_fn(scst_mcmd); struct iscsi_cmnd *req = (struct iscsi_cmnd *) scst_mgmt_cmd_get_tgt_priv(scst_mcmd); int status = iscsi_get_mgmt_response(scst_mgmt_cmd_get_status(scst_mcmd)); TRACE_MGMT_DBG("req %p, scst_mcmd %p, fn %d, scst status %d", - req, scst_mcmd, scst_mgmt_cmd_get_fn(scst_mcmd), - scst_mgmt_cmd_get_status(scst_mcmd)); - - iscsi_send_task_mgmt_resp(req, status); - - scst_mgmt_cmd_set_tgt_priv(scst_mcmd, NULL); + req, scst_mcmd, fn, scst_mgmt_cmd_get_status(scst_mcmd)); + switch (fn) { + case SCST_NEXUS_LOSS_SESS: + case SCST_ABORT_ALL_TASKS_SESS: + /* They are internal */ + break; + default: + iscsi_send_task_mgmt_resp(req, status); + scst_mgmt_cmd_set_tgt_priv(scst_mcmd, NULL); + break; + } return; } @@ -2930,6 +2938,7 @@ struct scst_tgt_template iscsi_template = { #endif .preprocessing_done = iscsi_preprocessing_done, .pre_exec = iscsi_pre_exec, + .task_mgmt_affected_cmds_done = iscsi_task_mgmt_affected_cmds_done, .task_mgmt_fn_done = iscsi_task_mgmt_fn_done, }; @@ -2953,7 +2962,7 @@ static __init int iscsi_run_threads(int count, char *name, int (*fn)(void *)) kfree(thr); goto out; } - list_add(&thr->threads_list_entry, &iscsi_threads_list); + list_add_tail(&thr->threads_list_entry, &iscsi_threads_list); } out: diff --git a/iscsi-scst/kernel/iscsi.h b/iscsi-scst/kernel/iscsi.h index 30d11a945..6049cf787 100644 --- a/iscsi-scst/kernel/iscsi.h +++ b/iscsi-scst/kernel/iscsi.h @@ -88,8 +88,9 @@ struct iscsi_session { struct iscsi_target *target; struct scst_session *scst_sess; - /* Both unprotected, since accessed only from a single read thread */ - struct list_head pending_list; + struct list_head pending_list; /* protected by sn_lock */ + + /* Unprotected, since accessed only from a single read thread */ u32 next_ttt; u32 max_queued_cmnds; /* unprotected, since read-only */ @@ -113,11 +114,12 @@ struct iscsi_session { struct list_head session_list_entry; - struct completion unreg_compl; + /* All don't need any protection */ + struct iscsi_session *sess_reinst_successor; + unsigned int sess_reinstating:1; /* All don't need any protection */ char *initiator_name; - unsigned int shutting_down:1; u64 sid; }; @@ -216,6 +218,12 @@ struct iscsi_conn { struct list_head conn_list_entry; /* list entry in session conn_list */ + /* All don't need any protection */ + struct iscsi_conn *conn_reinst_successor; + unsigned int conn_reinstating:1; + + struct completion ready_to_free; + /* Doesn't need any protection */ u16 cid; }; @@ -367,8 +375,9 @@ extern void conn_abort(struct iscsi_conn *conn); /* conn.c */ extern struct iscsi_conn *conn_lookup(struct iscsi_session *, u16); -extern int conn_add(struct iscsi_session *, struct conn_info *); -extern int conn_del(struct iscsi_session *, struct conn_info *); +extern void __iscsi_socket_bind(struct iscsi_conn *); +extern int conn_add(struct iscsi_session *, struct iscsi_kern_conn_info *); +extern int conn_del(struct iscsi_session *, struct iscsi_kern_conn_info *); extern int conn_free(struct iscsi_conn *); #define ISCSI_CONN_ACTIVE_CLOSE 1 @@ -387,12 +396,15 @@ extern void iscsi_put_page_callback(struct page *page); #endif extern int istrd(void *arg); extern int istwr(void *arg); +extern void iscsi_task_mgmt_affected_cmds_done(struct scst_mgmt_cmd *scst_mcmd); /* target.c */ struct iscsi_target *target_lookup_by_id(u32); -extern int target_add(struct target_info *); +extern int target_add(struct iscsi_kern_target_info *); extern int target_del(u32 id); -extern void target_del_all_sess(struct iscsi_target *target, bool deleting); +extern void target_del_session(struct iscsi_target *target, + struct iscsi_session *session, int flags); +extern void target_del_all_sess(struct iscsi_target *target, int flags); extern void target_del_all(void); extern struct seq_operations iscsi_seq_op; @@ -404,13 +416,14 @@ extern void iscsi_procfs_exit(void); /* session.c */ extern struct file_operations session_seq_fops; extern struct iscsi_session *session_lookup(struct iscsi_target *, u64); -extern int session_add(struct iscsi_target *, struct session_info *); +extern void sess_enable_reinstated_sess(struct iscsi_session *); +extern int session_add(struct iscsi_target *, struct iscsi_kern_session_info *); extern int session_del(struct iscsi_target *, u64); extern int session_free(struct iscsi_session *session); /* params.c */ -extern int iscsi_param_set(struct iscsi_target *, struct iscsi_param_info *, - int); +extern int iscsi_param_set(struct iscsi_target *, + struct iscsi_kern_param_info *, int); /* event.c */ extern int event_send(u32, u64, u32, u32, int); diff --git a/iscsi-scst/kernel/nthread.c b/iscsi-scst/kernel/nthread.c index 398b089ca..bc717fa4f 100644 --- a/iscsi-scst/kernel/nthread.c +++ b/iscsi-scst/kernel/nthread.c @@ -167,22 +167,6 @@ out: static inline void iscsi_check_closewait(struct iscsi_conn *conn) {}; #endif -static void iscsi_unreg_cmds_done_fn(struct scst_session *scst_sess) -{ - struct iscsi_session *sess = - (struct iscsi_session *)scst_sess_get_tgt_priv(scst_sess); - - TRACE_ENTRY(); - - TRACE_CONN_CLOSE_DBG("sess %p (scst_sess %p)", sess, scst_sess); - - sess->shutting_down = 1; - complete_all(&sess->unreg_compl); - - TRACE_EXIT(); - return; -} - static void free_pending_commands(struct iscsi_conn *conn) { struct iscsi_session *session = conn->session; @@ -219,6 +203,7 @@ static void free_pending_commands(struct iscsi_conn *conn) } } while (req_freed); spin_unlock(&session->sn_lock); + return; } @@ -258,6 +243,7 @@ static void free_orphaned_pending_commands(struct iscsi_conn *conn) } } while (req_freed); spin_unlock(&session->sn_lock); + return; } @@ -331,6 +317,42 @@ static void trace_conn_close(struct iscsi_conn *conn) static void trace_conn_close(struct iscsi_conn *conn) {} #endif /* CONFIG_SCST_DEBUG */ +void iscsi_task_mgmt_affected_cmds_done(struct scst_mgmt_cmd *scst_mcmd) +{ + int fn = scst_mgmt_cmd_get_fn(scst_mcmd); + void *priv = scst_mgmt_cmd_get_tgt_priv(scst_mcmd); + + TRACE_MGMT_DBG("scst_mcmd %p, fn %d, priv %p", scst_mcmd, fn, priv); + + switch (fn) { + case SCST_NEXUS_LOSS_SESS: + case SCST_ABORT_ALL_TASKS_SESS: + { + struct iscsi_conn *conn = (struct iscsi_conn *)priv; + struct iscsi_session *sess = conn->session; + + mutex_lock(&sess->target->target_mutex); + if (conn->conn_reinst_successor != NULL) { + sBUG_ON(!conn->conn_reinst_successor->conn_reinstating); + __iscsi_socket_bind(conn->conn_reinst_successor); + /* We will check for conn_reinst_successor later */ + } else if (sess->sess_reinst_successor != NULL) { + sess_enable_reinstated_sess(sess->sess_reinst_successor); + sess->sess_reinst_successor = NULL; + } + mutex_unlock(&sess->target->target_mutex); + + complete_all(&conn->ready_to_free); + break; + } + default: + /* Nothing to do */ + break; + } + + return; +} + /* No locks */ static void close_conn(struct iscsi_conn *conn) { @@ -339,6 +361,7 @@ static void close_conn(struct iscsi_conn *conn) typeof(jiffies) start_waiting = jiffies; typeof(jiffies) shut_start_waiting = start_waiting; bool pending_reported = 0, wait_expired = 0, shut_expired = 0; + bool free_sess; #define CONN_PENDING_TIMEOUT ((typeof(jiffies))10*HZ) #define CONN_WAIT_TIMEOUT ((typeof(jiffies))10*HZ) @@ -362,15 +385,26 @@ static void close_conn(struct iscsi_conn *conn) RCV_SHUTDOWN|SEND_SHUTDOWN); } - /* - * We need to call scst_unregister_session() ASAP to make SCST start - * recovering stuck commands. - * - * ToDo: this is incompatible with MC/S - */ - scst_unregister_session_ex(session->scst_sess, 0, - NULL, iscsi_unreg_cmds_done_fn); - session->scst_sess = NULL; + if (conn->conn_reinst_successor != NULL) { + int rc; + int lun = 0; + + /* Abort all outstanding commands */ + rc = scst_rx_mgmt_fn_lun(session->scst_sess, + SCST_ABORT_ALL_TASKS_SESS, (uint8_t *)&lun, sizeof(lun), + SCST_NON_ATOMIC, conn); + if (rc != 0) + PRINT_ERROR("SCST_ABORT_ALL_TASKS_SESS failed %d", rc); + } else { + int rc; + int lun = 0; + + rc = scst_rx_mgmt_fn_lun(session->scst_sess, + SCST_NEXUS_LOSS_SESS, (uint8_t *)&lun, sizeof(lun), + SCST_NON_ATOMIC, conn); + if (rc != 0) + PRINT_ERROR("SCST_NEXUS_LOSS_SESS failed %d", rc); + } if (conn->read_state != RX_INIT_BHS) { struct iscsi_cmnd *cmnd = conn->read_cmnd; @@ -401,18 +435,11 @@ static void close_conn(struct iscsi_conn *conn) mutex_unlock(&target->target_mutex); } + /* It's safe to check it without sn_lock */ if (!list_empty(&session->pending_list)) { TRACE_CONN_CLOSE_DBG("Disposing pending commands on " - "connection %p (conn_ref_cnt=%d)", - conn, - atomic_read(&conn->conn_ref_cnt)); - - /* - * Such complicated approach currently isn't really - * necessary, but it will be necessary for MC/S, if we - * won't want to reestablish the whole session on a - * connection failure. - */ + "connection %p (conn_ref_cnt=%d)", conn, + atomic_read(&conn->conn_ref_cnt)); free_pending_commands(conn); @@ -487,18 +514,27 @@ static void close_conn(struct iscsi_conn *conn) msleep(50); } + wait_for_completion(&conn->ready_to_free); + TRACE_CONN_CLOSE("Notifying user space about closing connection %p", conn); event_send(target->tid, session->sid, conn->cid, E_CONN_CLOSE, 0); - wait_for_completion(&session->unreg_compl); - - sBUG_ON(!session->shutting_down); + sBUG_ON(conn->conn_reinstating); + sBUG_ON(session->sess_reinstating); mutex_lock(&target->target_mutex); + + free_sess = (conn->conn_reinst_successor == NULL); + conn_free(conn); - /* ToDo: this is incompatible with MC/S */ - session_free(session); + + if (free_sess) { + sBUG_ON(session->sess_reinst_successor != NULL); + /* ToDo: this is incompatible with MC/S */ + session_free(session); + } + mutex_unlock(&target->target_mutex); TRACE_EXIT(); @@ -512,6 +548,11 @@ static int close_conn_thr(void *arg) TRACE_ENTRY(); #ifdef CONFIG_SCST_EXTRACHECKS + /* + * To satisfy iscsi_extracheck_is_rd_thread() in functions called + * on the connection close. It is safe, because at this point conn + * can't be used by any other thread. + */ conn->rd_task = current; #endif close_conn(conn); diff --git a/iscsi-scst/kernel/param.c b/iscsi-scst/kernel/param.c index 588bf1e6e..f865016da 100644 --- a/iscsi-scst/kernel/param.c +++ b/iscsi-scst/kernel/param.c @@ -92,7 +92,7 @@ static void log_params(struct iscsi_sess_param *param) } /* target_mutex supposed to be locked */ -static void sess_param_check(struct iscsi_param_info *info) +static void sess_param_check(struct iscsi_kern_param_info *info) { int32_t *iparam = info->session_param; @@ -114,7 +114,7 @@ static void sess_param_check(struct iscsi_param_info *info) /* target_mutex supposed to be locked */ static void sess_param_set(struct iscsi_sess_param *param, - struct iscsi_param_info *info) + struct iscsi_kern_param_info *info) { int32_t *iparam = info->session_param; @@ -140,7 +140,7 @@ static void sess_param_set(struct iscsi_sess_param *param, } static void sess_param_get(struct iscsi_sess_param *param, - struct iscsi_param_info *info) + struct iscsi_kern_param_info *info) { int32_t *iparam = info->session_param; @@ -166,7 +166,7 @@ static void sess_param_get(struct iscsi_sess_param *param, } /* target_mutex supposed to be locked */ -static void trgt_param_check(struct iscsi_param_info *info) +static void trgt_param_check(struct iscsi_kern_param_info *info) { int32_t *iparam = info->target_param; @@ -176,7 +176,7 @@ static void trgt_param_check(struct iscsi_param_info *info) /* target_mutex supposed to be locked */ static void trgt_param_set(struct iscsi_target *target, - struct iscsi_param_info *info) + struct iscsi_kern_param_info *info) { struct iscsi_trgt_param *param = &target->trgt_param; int32_t *iparam = info->target_param; @@ -186,7 +186,7 @@ static void trgt_param_set(struct iscsi_target *target, /* target_mutex supposed to be locked */ static void trgt_param_get(struct iscsi_trgt_param *param, - struct iscsi_param_info *info) + struct iscsi_kern_param_info *info) { int32_t *iparam = info->target_param; @@ -195,7 +195,7 @@ static void trgt_param_get(struct iscsi_trgt_param *param, /* target_mutex supposed to be locked */ static int trgt_param(struct iscsi_target *target, - struct iscsi_param_info *info, int set) + struct iscsi_kern_param_info *info, int set) { if (set) { struct iscsi_trgt_param *prm; @@ -213,7 +213,7 @@ static int trgt_param(struct iscsi_target *target, /* target_mutex supposed to be locked */ static int sess_param(struct iscsi_target *target, - struct iscsi_param_info *info, int set) + struct iscsi_kern_param_info *info, int set) { struct iscsi_session *session = NULL; struct iscsi_sess_param *param; @@ -247,8 +247,8 @@ out: } /* target_mutex supposed to be locked */ -int iscsi_param_set(struct iscsi_target *target, struct iscsi_param_info *info, - int set) +int iscsi_param_set(struct iscsi_target *target, + struct iscsi_kern_param_info *info, int set) { int err; diff --git a/iscsi-scst/kernel/session.c b/iscsi-scst/kernel/session.c index 9db601894..f81688d88 100644 --- a/iscsi-scst/kernel/session.c +++ b/iscsi-scst/kernel/session.c @@ -23,7 +23,7 @@ struct iscsi_session *session_lookup(struct iscsi_target *target, u64 sid) list_for_each_entry(session, &target->session_list, session_list_entry) { - if ((session->sid == sid) && !session->shutting_down) + if (session->sid == sid) return session; } return NULL; @@ -31,7 +31,7 @@ struct iscsi_session *session_lookup(struct iscsi_target *target, u64 sid) /* target_mutex supposed to be locked */ static int iscsi_session_alloc(struct iscsi_target *target, - struct session_info *info) + struct iscsi_kern_session_info *info, struct iscsi_session **result) { int err; unsigned int i; @@ -93,14 +93,15 @@ static int iscsi_session_alloc(struct iscsi_target *target, kfree(name); scst_sess_set_tgt_priv(session->scst_sess, session); - init_completion(&session->unreg_compl); - list_add(&session->session_list_entry, &target->session_list); + list_add_tail(&session->session_list_entry, &target->session_list); TRACE_MGMT_DBG("Session %p created: target %p, tid %u, sid %#Lx", session, target, target->tid, info->sid); + *result = session; return 0; + err: if (session) { kfree(session->initiator_name); @@ -111,20 +112,89 @@ err: } /* target_mutex supposed to be locked */ -int session_add(struct iscsi_target *target, struct session_info *info) +void sess_enable_reinstated_sess(struct iscsi_session *sess) { - struct iscsi_session *session; - int err = -EEXIST; + struct iscsi_conn *c; + + TRACE_ENTRY(); + + TRACE_MGMT_DBG("Enabling reinstate successor sess %p", sess); + + sBUG_ON(!sess->sess_reinstating); + + list_for_each_entry(c, &sess->conn_list, conn_list_entry) { + __iscsi_socket_bind(c); + } + sess->sess_reinstating = 0; + + TRACE_EXIT(); + return; +} + +/* target_mutex supposed to be locked */ +static void session_reinstate(struct iscsi_session *old_sess, + struct iscsi_session *new_sess) +{ + TRACE_ENTRY(); + + TRACE_MGMT_DBG("Reinstating sess %p with SID %llx (old %p, SID %llx)", + new_sess, new_sess->sid, old_sess, old_sess->sid); + + new_sess->sess_reinstating = 1; + old_sess->sess_reinst_successor = new_sess; + + scst_set_initial_UA(new_sess->scst_sess, + SCST_LOAD_SENSE(scst_sense_nexus_loss_UA)); + + target_del_session(old_sess->target, old_sess, 0); + + TRACE_EXIT(); + return; +} + +/* target_mutex supposed to be locked */ +int session_add(struct iscsi_target *target, + struct iscsi_kern_session_info *info) +{ + struct iscsi_session *session, *old_sess; + int err = 0; + union iscsi_sid sid; + + TRACE_MGMT_DBG("Adding session SID %llx", info->sid); session = session_lookup(target, info->sid); if (session) { PRINT_ERROR("Attempt to add session with existing SID %llx", info->sid); - return err; + err = -EEXIST; + goto out; + } + + sid = (union iscsi_sid)info->sid; + sid.id.tsih = 0; + old_sess = NULL; + + /* + * We need to find the latest session to correctly handle + * multi-reinstatements + */ + list_for_each_entry_reverse(session, &target->session_list, + session_list_entry) { + union iscsi_sid i = (union iscsi_sid)session->sid; + i.id.tsih = 0; + if ((sid.id64 == i.id64) && + (strcmp(info->initiator_name, session->initiator_name) == 0)) { + /* session reinstatement */ + old_sess = session; + break; + } } - err = iscsi_session_alloc(target, info); + err = iscsi_session_alloc(target, info, &session); + if ((err == 0) && (old_sess != NULL)) + session_reinstate(old_sess, session); +out: return err; } @@ -133,8 +203,8 @@ int session_free(struct iscsi_session *session) { unsigned int i; - TRACE_MGMT_DBG("Freeing session %p:%#Lx", - session, (long long unsigned int)session->sid); + TRACE_MGMT_DBG("Freeing session %p (SID %llx)", + session, session->sid); sBUG_ON(!list_empty(&session->conn_list)); if (unlikely(atomic_read(&session->active_cmds) != 0)) { @@ -149,6 +219,21 @@ int session_free(struct iscsi_session *session) if (session->scst_sess != NULL) scst_unregister_session(session->scst_sess, 1, NULL); + if (session->sess_reinst_successor != NULL) + sess_enable_reinstated_sess(session->sess_reinst_successor); + + if (session->sess_reinstating) { + struct iscsi_session *s; + TRACE_MGMT_DBG("Freeing being reinstated sess %p", session); + list_for_each_entry(s, &session->target->session_list, + session_list_entry) { + if (s->sess_reinst_successor == session) { + s->sess_reinst_successor = NULL; + break; + } + } + } + list_del(&session->session_list_entry); kfree(session->initiator_name); @@ -183,10 +268,10 @@ static void iscsi_session_info_show(struct seq_file *seq, list_for_each_entry(session, &target->session_list, session_list_entry) { - seq_printf(seq, "\tsid:%llx initiator:%s shutting down %d\n", + seq_printf(seq, "\tsid:%llx initiator:%s reinstating %d\n", (long long unsigned int)session->sid, session->initiator_name, - session->shutting_down); + session->sess_reinstating); conn_info_show(seq, session); } return; diff --git a/iscsi-scst/kernel/target.c b/iscsi-scst/kernel/target.c index 483ef65b6..a6d8dd362 100644 --- a/iscsi-scst/kernel/target.c +++ b/iscsi-scst/kernel/target.c @@ -28,32 +28,6 @@ static LIST_HEAD(target_list); static u32 next_target_id; static u32 nr_targets; -static struct iscsi_sess_param default_session_param = { - .initial_r2t = 1, - .immediate_data = 1, - .max_connections = 1, - .max_recv_data_length = 8192, - .max_xmit_data_length = 8192, - .max_burst_length = 262144, - .first_burst_length = 65536, - .default_wait_time = 2, - .default_retain_time = 20, - .max_outstanding_r2t = 1, - .data_pdu_inorder = 1, - .data_sequence_inorder = 1, - .error_recovery_level = 0, - .header_digest = DIGEST_NONE, - .data_digest = DIGEST_NONE, - .ofmarker = 0, - .ifmarker = 0, - .ofmarkint = 2048, - .ifmarkint = 2048, -}; - -static struct iscsi_trgt_param default_target_param = { - .queued_cmnds = DEFAULT_NR_QUEUED_CMNDS, -}; - /* target_mgmt_mutex supposed to be locked */ struct iscsi_target *target_lookup_by_id(u32 id) { @@ -79,7 +53,7 @@ static struct iscsi_target *target_lookup_by_name(char *name) } /* target_mgmt_mutex supposed to be locked */ -static int iscsi_target_create(struct target_info *info, u32 tid) +static int iscsi_target_create(struct iscsi_kern_target_info *info, u32 tid) { int err = -EINVAL, len; char *name = info->name; @@ -106,11 +80,6 @@ static int iscsi_target_create(struct target_info *info, u32 tid) target->tid = info->tid = tid; - memcpy(&target->trgt_sess_param, &default_session_param, - sizeof(default_session_param)); - memcpy(&target->trgt_param, &default_target_param, - sizeof(default_target_param)); - strncpy(target->name, name, sizeof(target->name) - 1); mutex_init(&target->target_mutex); @@ -123,7 +92,7 @@ static int iscsi_target_create(struct target_info *info, u32 tid) goto out_free; } - list_add(&target->target_list_entry, &target_list); + list_add_tail(&target->target_list_entry, &target_list); return 0; @@ -138,7 +107,7 @@ out: } /* target_mgmt_mutex supposed to be locked */ -int target_add(struct target_info *info) +int target_add(struct iscsi_kern_target_info *info) { int err = -EEXIST; u32 tid = info->tid; @@ -215,15 +184,13 @@ out: return err; } -static void target_del_session(struct iscsi_target *target, - struct iscsi_session *session, bool deleting) +void target_del_session(struct iscsi_target *target, + struct iscsi_session *session, int flags) { - int flags = ISCSI_CONN_ACTIVE_CLOSE; + TRACE_ENTRY(); - if (deleting) - flags |= ISCSI_CONN_DELETING; + TRACE_MGMT_DBG("Deleting session %p", session); - TRACE_MGMT_DBG("Cleaning up session %p", session); if (!list_empty(&session->conn_list)) { struct iscsi_conn *conn, *tc; list_for_each_entry_safe(conn, tc, &session->conn_list, @@ -236,10 +203,13 @@ static void target_del_session(struct iscsi_target *target, session); session_del(target, session->sid); } + + TRACE_EXIT(); + return; } /* target_mutex supposed to be locked */ -void target_del_all_sess(struct iscsi_target *target, bool deleting) +void target_del_all_sess(struct iscsi_target *target, int flags) { struct iscsi_session *session, *ts; @@ -249,7 +219,7 @@ void target_del_all_sess(struct iscsi_target *target, bool deleting) TRACE_MGMT_DBG("Deleting all sessions from target %p", target); list_for_each_entry_safe(session, ts, &target->session_list, session_list_entry) { - target_del_session(target, session, deleting); + target_del_session(target, session, flags); } } @@ -276,7 +246,9 @@ void target_del_all(void) target_list_entry) { mutex_lock(&target->target_mutex); if (!list_empty(&target->session_list)) { - target_del_all_sess(target, true); + target_del_all_sess(target, + ISCSI_CONN_ACTIVE_CLOSE | + ISCSI_CONN_DELETING); mutex_unlock(&target->target_mutex); } else { TRACE_MGMT_DBG("Deleting target %p", target); diff --git a/iscsi-scst/usr/Makefile b/iscsi-scst/usr/Makefile index 24605dafd..9710bb172 100644 --- a/iscsi-scst/usr/Makefile +++ b/iscsi-scst/usr/Makefile @@ -21,8 +21,11 @@ OBJS_D = $(SRCS_D:.c=.o) SRCS_ADM = iscsi_adm.c param.c OBJS_ADM = $(SRCS_ADM:.c=.o) -CFLAGS += -O2 -fno-inline -Wall -Wstrict-prototypes -g -I../include +CFLAGS += -O2 -fno-inline -Wall -Wextra -Wstrict-prototypes -Wno-sign-compare \ + -Werror=implicit-function-declaration -Wno-unused-parameter \ + -Wno-missing-field-initializers -g -I../include CFLAGS += -D_GNU_SOURCE # required for glibc >= 2.8 + PROGRAMS = iscsi-scstd iscsi-scst-adm LIBS = -lcrypto diff --git a/iscsi-scst/usr/conn.c b/iscsi-scst/usr/conn.c index a1a82bd15..f5c22960e 100644 --- a/iscsi-scst/usr/conn.c +++ b/iscsi-scst/usr/conn.c @@ -31,101 +31,46 @@ struct connection *conn_alloc(void) { struct connection *conn; - if (!(conn = malloc(sizeof(*conn)))) - return NULL; + conn = malloc(sizeof(*conn)); + if (conn == NULL) + goto out; memset(conn, 0, sizeof(*conn)); conn->state = STATE_FREE; param_set_defaults(conn->session_param, session_keys); INIT_LIST_HEAD(&conn->rsp_buf_list); +out: return conn; } void conn_free(struct connection *conn) { + remque(&conn->clist); free(conn->initiator); free(conn->user); free(conn); + return; } -int conn_test(struct connection *conn) -{ - FILE *f; - char buf[8192], *p; - u32 tid, t_tid, cid, t_cid; - u64 sid, t_sid; - int err = -ENOENT, find = 0; - - t_tid = conn->tid; - t_sid = conn->session->sid.id64; - t_cid = conn->cid; - - if ((f = fopen(PROC_SESSION, "r")) == NULL) { - fprintf(stderr, "Can't open %s\n", PROC_SESSION); - return -errno; - } - - while (fgets(buf, sizeof(buf), f)) { - p = buf; - while (isspace((int) *p)) - p++; - - if (!strncmp(p, "tid:", 4)) { - if (sscanf(p, "tid:%u", &tid) != 1) { - err = -EIO; - goto out; - } - if (tid == t_tid) - find = 1; - else - find = 0; - } else if (!strncmp(p, "sid:", 4)) { - if (!find) - continue; - if (sscanf(p, "sid:%" SCNu64, &sid) != 1) { - err = -EIO; - goto out; - } - - if (sid == t_sid) - find = 1; - else - find = 0; - } else if (!strncmp(p, "cid:", 4)) { - if (!find) - continue; - if (sscanf(p, "cid:%u", &cid) != 1) { - err = -EIO; - goto out; - } - - if (cid == t_cid) { - err = 0; - goto out; - } - } - } - -out: - fclose(f); - - return err; -} - -void conn_take_fd(struct connection *conn, int fd) +void conn_pass_to_kern(struct connection *conn, int fd) { int err; - log_debug(1, "conn_take_fd: %d %u %u %u %" PRIx64, - fd, conn->cid, conn->stat_sn, conn->exp_stat_sn, conn->sid.id64); - conn->session->conn_cnt++; + log_debug(1, "fd %d, cid %u, stat_sn %u, exp_stat_sn %u sid%" PRIx64, + fd, conn->cid, conn->stat_sn, conn->exp_stat_sn, conn->sid.id64); - err = ki->conn_create(conn->tid, conn->session->sid.id64, conn->cid, + err = kernel_conn_create(conn->tid, conn->sess->sid.id64, conn->cid, conn->stat_sn, conn->exp_stat_sn, fd, conn->session_param[key_header_digest].val, conn->session_param[key_data_digest].val); + if (err == 0) + conn->sess->kern_conn_cnt++; + else + log_error("kernel_conn_create() failed: %s", strerror(errno)); + + /* We don't need to return err, because we are going to close conn anyway */ return; } @@ -134,6 +79,7 @@ void conn_read_pdu(struct connection *conn) conn->iostate = IOSTATE_READ_BHS; conn->buffer = (void *)&conn->req.bhs; conn->rwsize = BHS_SIZE; + return; } void conn_write_pdu(struct connection *conn) @@ -142,6 +88,7 @@ void conn_write_pdu(struct connection *conn) memset(&conn->rsp, 0, sizeof(conn->rsp)); conn->buffer = (void *)&conn->rsp.bhs; conn->rwsize = BHS_SIZE; + return; } void conn_free_rsp_buf_list(struct connection *conn) @@ -155,6 +102,7 @@ void conn_free_rsp_buf_list(struct connection *conn) conn->rsp.datasize = 0; conn->rsp.data = NULL; + return; } void conn_free_pdu(struct connection *conn) @@ -169,4 +117,5 @@ void conn_free_pdu(struct connection *conn) conn->rsp.ahs = NULL; } conn_free_rsp_buf_list(conn); + return; } diff --git a/iscsi-scst/usr/ctldev.c b/iscsi-scst/usr/ctldev.c index 22c046012..f5d6a83dd 100644 --- a/iscsi-scst/usr/ctldev.c +++ b/iscsi-scst/usr/ctldev.c @@ -34,7 +34,7 @@ struct session_file_operations { int (*connection_op) (int fd, u32 tid, u64 sid, u32 cid, void *arg); }; -static int ctrdev_open(int *max_data_seg_len) +int kernel_open(int *max_data_seg_len) { FILE *f; char devname[256]; @@ -42,7 +42,7 @@ static int ctrdev_open(int *max_data_seg_len) int devn; int ctlfd = -1; int err; - struct iscsi_register_info reg = { 0 }; + struct iscsi_kern_register_info reg = { 0 }; if (!(f = fopen("/proc/devices", "r"))) { perror("Cannot open control path to the driver"); @@ -104,10 +104,10 @@ out_close: goto out; } -static int iscsi_target_create(u32 *tid, char *name) +int kernel_target_create(u32 *tid, char *name) { int err; - struct target_info info; + struct iscsi_kern_target_info info; memset(&info, 0, sizeof(info)); @@ -122,9 +122,9 @@ static int iscsi_target_create(u32 *tid, char *name) return err; } -static int iscsi_target_destroy(u32 tid) +int kernel_target_destroy(u32 tid) { - struct target_info info; + struct iscsi_kern_target_info info; int res; memset(&info, 0, sizeof(info)); @@ -137,10 +137,10 @@ static int iscsi_target_destroy(u32 tid) return res; } -static int iscsi_conn_destroy(u32 tid, u64 sid, u32 cid) +int kernel_conn_destroy(u32 tid, u64 sid, u32 cid) { int err; - struct conn_info info; + struct iscsi_kern_conn_info info; info.tid = tid; info.sid = sid; @@ -152,173 +152,14 @@ static int iscsi_conn_destroy(u32 tid, u64 sid, u32 cid) return err; } -static int __conn_close(int fd, u32 tid, u64 sid, u32 cid, void *arg) -{ - return ki->conn_destroy(tid, sid, cid); -} - -static int __target_del(int fd, u32 tid, void *arg) -{ - return ki->target_destroy(tid); -} - -static int proc_session_parse(int fd, struct session_file_operations *ops, - int op_tid, void *arg) -{ - FILE *f; - char buf[8192], *p; - u32 tid, cid; - u64 sid; - int err, skip, done = 0; - - if ((f = fopen(PROC_SESSION, "r")) == NULL) { - fprintf(stderr, "Can't open %s\n", PROC_SESSION); - return errno; - } - - skip = 0; - while (fgets(buf, sizeof(buf), f)) { - p = buf; - while (isspace((int) *p)) - p++; - - if (!strncmp(p, "tid:", 4)) { - if (sscanf(p, "tid:%u", &tid) != 1) - break; - if (op_tid != -1) { - if (tid == op_tid) - skip = 0; - else { - skip = 1; - if (done) - break; - else - continue; - } - } - if (ops->target_op) - if ((err = ops->target_op(fd, tid, arg)) < 0) - goto out; - continue; - } - if (skip) - continue; - if (!strncmp(p, "sid:", 4)) { - if (sscanf(p, "sid:%" SCNu64, &sid) != 1) { - log_error("Unknown %s sid syntax: %s\n", PROC_SESSION, p); - break; - } - - if (ops->session_op) - if ((err = ops->session_op(fd, tid, sid, arg)) < 0) - goto out; - } else if (!strncmp(p, "cid:", 4)) { - if (sscanf(p, "cid:%u", &cid) != 1) { - log_error("Unknown %s cid syntax: %s\n", PROC_SESSION, p); - break; - } - if (ops->connection_op) - if ((err = ops->connection_op(fd, tid, sid, cid, arg)) < 0) - goto out; - } else - log_error("Unknown %s string: %s\n", PROC_SESSION, p); - - done = 1; - } - - err = 0; -out: - fclose(f); - - return err; -} - -static int session_retry (int fd, u32 tid, u64 sid, void *arg) -{ - return -EAGAIN; -} - -static int conn_retry (int fd, u32 tid, u64 sid, u32 cid, void *arg) -{ - return -EAGAIN; -} - -static int __sess_cleanup(int fd, u32 tid, void *arg) -{ - wait_4_iscsi_event(100); - return 0; -} - -static struct session_file_operations conn_close_ops = { - .connection_op = __conn_close, -}; - -static struct session_file_operations conn_sess_cleanup_ops = { - .target_op = __sess_cleanup, -}; - -static struct session_file_operations shutdown_wait_ops = { - .session_op = session_retry, - .connection_op = conn_retry, -}; - -static struct session_file_operations target_del_ops = { - .target_op = __target_del, -}; - -int target_destroy(u32 tid) -{ - int err; - - conn_blocked = 1; - - proc_session_parse(ctrl_fd, &conn_close_ops, tid, NULL); - - while (proc_session_parse(ctrl_fd, &shutdown_wait_ops, tid, NULL) < 0) { - sleep(1); - } - proc_session_parse(ctrl_fd, &conn_sess_cleanup_ops, tid, NULL); - - err = proc_session_parse(ctrl_fd, &target_del_ops, tid, NULL); - - conn_blocked = 0; - - return err; -} - struct session_conn_close_arg { u64 sid; }; -static int session_conn_close(int fd, u32 tid, u64 sid, u32 cid, void *opaque) -{ - struct session_conn_close_arg *arg = (struct session_conn_close_arg *) opaque; - int err; - - if (arg->sid == sid) - err = ki->conn_destroy(tid, sid, cid); - - return 0; -} - -struct session_file_operations session_conns_close_ops = { - .connection_op = session_conn_close, -}; - -int session_conns_close(u32 tid, u64 sid) -{ - int err; - struct session_conn_close_arg arg = {sid}; - - err = proc_session_parse(ctrl_fd, &session_conns_close_ops, tid, &arg); - - return err; -} - -static int iscsi_param_get(u32 tid, u64 sid, int type, struct iscsi_param *param) +int kernel_param_get(u32 tid, u64 sid, int type, struct iscsi_param *param) { int err, i; - struct iscsi_param_info info; + struct iscsi_kern_param_info info; memset(&info, 0, sizeof(info)); info.tid = tid; @@ -341,11 +182,11 @@ static int iscsi_param_get(u32 tid, u64 sid, int type, struct iscsi_param *param return err; } -static int iscsi_param_set(u32 tid, u64 sid, int type, u32 partial, +int kernel_param_set(u32 tid, u64 sid, int type, u32 partial, struct iscsi_param *param) { int i, err; - struct iscsi_param_info info; + struct iscsi_kern_param_info info; memset(&info, 0, sizeof(info)); info.tid = tid; @@ -369,10 +210,10 @@ static int iscsi_param_set(u32 tid, u64 sid, int type, u32 partial, return err; } -static int iscsi_session_create(u32 tid, u64 sid, u32 exp_cmd_sn, +int kernel_session_create(u32 tid, u64 sid, u32 exp_cmd_sn, char *name, char *user) { - struct session_info info; + struct iscsi_kern_session_info info; int res; memset(&info, 0, sizeof(info)); @@ -390,9 +231,9 @@ static int iscsi_session_create(u32 tid, u64 sid, u32 exp_cmd_sn, return res; } -static int iscsi_session_destroy(u32 tid, u64 sid) +int kernel_session_destroy(u32 tid, u64 sid) { - struct session_info info; + struct iscsi_kern_session_info info; int res; memset(&info, 0, sizeof(info)); @@ -410,10 +251,10 @@ static int iscsi_session_destroy(u32 tid, u64 sid) return res; } -static int iscsi_conn_create(u32 tid, u64 sid, u32 cid, u32 stat_sn, u32 exp_stat_sn, +int kernel_conn_create(u32 tid, u64 sid, u32 cid, u32 stat_sn, u32 exp_stat_sn, int fd, u32 hdigest, u32 ddigest) { - struct conn_info info; + struct iscsi_kern_conn_info info; int res; memset(&info, 0, sizeof(info)); @@ -433,17 +274,3 @@ static int iscsi_conn_create(u32 tid, u64 sid, u32 cid, u32 stat_sn, u32 exp_sta return res; } - -struct iscsi_kernel_interface ioctl_ki = { - .ctldev_open = ctrdev_open, - .param_get = iscsi_param_get, - .param_set = iscsi_param_set, - .target_create = iscsi_target_create, - .target_destroy = iscsi_target_destroy, - .session_create = iscsi_session_create, - .session_destroy = iscsi_session_destroy, - .conn_create = iscsi_conn_create, - .conn_destroy = iscsi_conn_destroy, -}; - -struct iscsi_kernel_interface *ki = &ioctl_ki; diff --git a/iscsi-scst/usr/event.c b/iscsi-scst/usr/event.c index 539f3290b..ec6bf400b 100644 --- a/iscsi-scst/usr/event.c +++ b/iscsi-scst/usr/event.c @@ -80,7 +80,7 @@ static int nl_read(int fd, void *data, int len) void handle_iscsi_events(int fd) { struct session *session; - struct iscsi_event event; + struct iscsi_kern_event event; int res; retry: @@ -103,8 +103,9 @@ retry: goto retry; } - if (!--session->conn_cnt) - session_remove(session); + session->kern_conn_cnt--; + if ((session->kern_conn_cnt == 0) && list_empty(&session->conn_list)) + session_free(session); break; default: log_warning("%s(%d) %u\n", __FUNCTION__, __LINE__, event.state); diff --git a/iscsi-scst/usr/iscsi_scstd.c b/iscsi-scst/usr/iscsi_scstd.c index b300d05e5..9720dac37 100644 --- a/iscsi-scst/usr/iscsi_scstd.c +++ b/iscsi-scst/usr/iscsi_scstd.c @@ -208,39 +208,75 @@ static void create_listen_socket(struct pollfd *array) static void accept_connection(int listen) { - struct sockaddr_storage from; + struct sockaddr_storage from, to; + char portal[50]; /* for full IP6 address + port */ socklen_t namesize; struct pollfd *pollfd; struct connection *conn; - int fd, i; + int fd, i, rc; namesize = sizeof(from); - if ((fd = accept(listen, (struct sockaddr *) &from, &namesize)) < 0) { - if (errno != EINTR && errno != EAGAIN) { + if ((fd = accept(listen, (struct sockaddr *)&from, &namesize)) < 0) { + switch (errno) { + case EINTR: + case EAGAIN: + case ENETDOWN: + case EPROTO: + case ENOPROTOOPT: + case EHOSTDOWN: + case ENONET: + case EHOSTUNREACH: + case EOPNOTSUPP: + case ENETUNREACH: + break; + default: perror("accept(incoming_socket) failed"); exit(1); } - return; + goto out; + } + + namesize = sizeof(to); + rc = getsockname(fd, (struct sockaddr *)&to, &namesize); + if (rc == 0) { + if (from.ss_family == AF_INET) { + struct sockaddr_in *in = (struct sockaddr_in *)&to; + rc = snprintf(portal, sizeof(portal), "%s:%hu", + inet_ntoa(in->sin_addr), ntohs(in->sin_port)); + } else if (from.ss_family == AF_INET6) { + struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&to; + rc = snprintf(portal, sizeof(portal), "%x:%x:%x:%x:%x:%x:%x:%x.%hu", + in6->sin6_addr.s6_addr16[7], in6->sin6_addr.s6_addr16[6], + in6->sin6_addr.s6_addr16[5], in6->sin6_addr.s6_addr16[4], + in6->sin6_addr.s6_addr16[3], in6->sin6_addr.s6_addr16[2], + in6->sin6_addr.s6_addr16[1], in6->sin6_addr.s6_addr16[0], + ntohs(in6->sin6_port)); + } + if (rc >= sizeof(portal)) + log_error("portal too small %zu (needed %d)", sizeof(portal), rc); + } else { + portal[0] = '\0'; + perror("getsockname() failed"); + goto out_close; } if (from.ss_family == AF_INET) { struct sockaddr_in *in = (struct sockaddr_in *)&from; - log_info("Connect from %s:%hu", inet_ntoa(in->sin_addr), - ntohs(in->sin_port)); + log_info("Connect from %s:%hu to %s", inet_ntoa(in->sin_addr), + ntohs(in->sin_port), portal); } else if (from.ss_family == AF_INET6) { struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&from; - log_info("Connect from %x:%x:%x:%x:%x:%x:%x:%x.%hu", + log_info("Connect from %x:%x:%x:%x:%x:%x:%x:%x.%hu to %s", in6->sin6_addr.s6_addr16[7], in6->sin6_addr.s6_addr16[6], in6->sin6_addr.s6_addr16[5], in6->sin6_addr.s6_addr16[4], in6->sin6_addr.s6_addr16[3], in6->sin6_addr.s6_addr16[2], in6->sin6_addr.s6_addr16[1], in6->sin6_addr.s6_addr16[0], - ntohs(in6->sin6_port)); + ntohs(in6->sin6_port), portal); } if (conn_blocked) { - log_warning("A connection refused\n"); - close(fd); - return; + log_warning("Connection refused due to blocking\n"); + goto out_close; } for (i = 0; i < INCOMING_MAX; i++) { @@ -248,14 +284,15 @@ static void accept_connection(int listen) break; } if (i >= INCOMING_MAX) { - log_error("unable to find incoming slot? %d\n", i); - exit(1); + log_error("Unable to find incoming slot? %d\n", i); + goto out_close; } if (!(conn = conn_alloc())) { - log_error("fail to allocate %s", "conn\n"); - exit(1); + log_error("Fail to allocate %s", "conn\n"); + goto out_close; } + conn->fd = fd; incoming[i] = conn; conn_read_pdu(conn); @@ -269,6 +306,13 @@ static void accept_connection(int listen) incoming_cnt++; if (incoming_cnt >= INCOMING_MAX) poll_array[POLL_LISTEN].events = 0; + +out: + return; + +out_close: + close(fd); + goto out; } static void __set_fd(int idx, int fd) @@ -396,7 +440,7 @@ static void event_conn(struct connection *conn, struct pollfd *pollfd) switch (conn->state) { case STATE_KERNEL: - conn_take_fd(conn, pollfd->fd); + conn_pass_to_kern(conn, pollfd->fd); conn->state = STATE_CLOSE; break; case STATE_EXIT: @@ -522,7 +566,7 @@ static void event_loop(int timeout) event_conn(conn, pollfd); if (conn->state == STATE_CLOSE) { - log_debug(0, "connection closed"); + log_debug(0, "closing conn %p", conn); conn_free_pdu(conn); conn_free(conn); close(pollfd->fd); @@ -536,33 +580,33 @@ static void event_loop(int timeout) void init_max_data_seg_len(int max_data_seg_len) { - if ((session_keys[3].local_def != -1) || - (session_keys[3].max != -1) || - (session_keys[4].local_def != -1) || - (session_keys[4].max != -1) || - (session_keys[5].local_def != -1) || - (session_keys[5].max != -1) || - (session_keys[6].local_def != -1) || - (session_keys[6].max != -1)) { + if ((session_keys[key_max_recv_data_length].local_def != -1) || + (session_keys[key_max_recv_data_length].max != -1) || + (session_keys[key_max_xmit_data_length].local_def != -1) || + (session_keys[key_max_xmit_data_length].max != -1) || + (session_keys[key_max_burst_length].local_def != -1) || + (session_keys[key_max_burst_length].max != -1) || + (session_keys[key_first_burst_length].local_def != -1) || + (session_keys[key_first_burst_length].max != -1)) { log_error("Wrong session_keys initialization"); exit(-1); } /* MaxRecvDataSegmentLength */ - session_keys[3].local_def = max_data_seg_len; - session_keys[3].max = max_data_seg_len; + session_keys[key_max_recv_data_length].local_def = max_data_seg_len; + session_keys[key_max_recv_data_length].max = max_data_seg_len; /* MaxXmitDataSegmentLength */ - session_keys[4].local_def = max_data_seg_len; - session_keys[4].max = max_data_seg_len; + session_keys[key_max_xmit_data_length].local_def = max_data_seg_len; + session_keys[key_max_xmit_data_length].max = max_data_seg_len; /* MaxBurstLength */ - session_keys[5].local_def = max_data_seg_len; - session_keys[5].max = max_data_seg_len; + session_keys[key_max_burst_length].local_def = max_data_seg_len; + session_keys[key_max_burst_length].max = max_data_seg_len; /* FirstBurstLength */ - session_keys[6].local_def = max_data_seg_len; - session_keys[6].max = max_data_seg_len; + session_keys[key_first_burst_length].local_def = max_data_seg_len; + session_keys[key_first_burst_length].max = max_data_seg_len; return; } @@ -607,6 +651,10 @@ int main(int argc, char **argv) break; case 'a': server_address = strdup(optarg); + if (server_address == NULL) { + perror("strdup failed"); + exit(-1); + } break; case 'p': server_port = (uint16_t)strtoul(optarg, NULL, 0); @@ -629,7 +677,7 @@ int main(int argc, char **argv) exit(-1); }; - if ((ctrl_fd = ki->ctldev_open(&max_data_seg_len)) < 0) + if ((ctrl_fd = kernel_open(&max_data_seg_len)) < 0) exit(-1); init_max_data_seg_len(max_data_seg_len); diff --git a/iscsi-scst/usr/iscsid.c b/iscsi-scst/usr/iscsid.c index 6a6b4a88b..3304a12ad 100644 --- a/iscsi-scst/usr/iscsid.c +++ b/iscsi-scst/usr/iscsid.c @@ -241,44 +241,82 @@ static void text_scan_security(struct connection *conn) rsp->status_detail = ISCSI_STATUS_AUTH_FAILED; conn->state = STATE_EXIT; } + return; } -static void login_security_done(struct connection *conn) +static int login_check_reinstatement(struct connection *conn) { - int err; struct iscsi_login_req_hdr *req = (struct iscsi_login_req_hdr *)&conn->req.bhs; struct iscsi_login_rsp_hdr *rsp = (struct iscsi_login_rsp_hdr *)&conn->rsp.bhs; struct session *session; + int res = 0; - if (!conn->tid) - return; + /* + * We only check here to catch errors earlier. Actual session/connection + * reinstatement, if necessary, will be done in the kernel. + */ - if ((session = session_find_name(conn->tid, conn->initiator, req->sid))) { - if (!req->sid.id.tsih) { - /* do session reinstatement */ - session_conns_close(conn->tid, session->sid.id64); - session = NULL; + sBUG_ON(conn->sess != NULL); + + session = session_find_name(conn->tid, conn->initiator, req->sid); + if (session != NULL) { + if (req->sid.id.tsih == 0) { + /* Kernel will do session reinstatement */ + log_debug(1, "Session sid %#" PRIx64 " reinstatement " + "detected (tid %d, initiator %s)", req->sid.id64, + conn->tid, conn->initiator); } else if (req->sid.id.tsih != session->sid.id.tsih) { - /* fail the login */ + log_error("TSIH for existing session sid %#" PRIx64 + ") doesn't match (tid %d, initiator %s, sid requested " + "%#" PRIx64, session->sid.id64, conn->tid, + conn->initiator, req->sid.id64); + /* Fail the login */ rsp->status_class = ISCSI_STATUS_INITIATOR_ERR; rsp->status_detail = ISCSI_STATUS_SESSION_NOT_FOUND; conn->state = STATE_EXIT; - return; - } else if ((err = conn_test(conn)) == -ENOENT) { - /* do connection reinstatement */ + res = -1; + goto out; + } else { + struct connection *c = conn_find(session, conn->cid); + if (c != NULL) { + /* Kernel will do connection reinstatement */ + log_debug(1, "Conn %x reinstatement " + "detected (tid %d, sid %#" PRIx64 + "initiator %s)", conn->cid, conn->tid, + req->sid.id64, conn->initiator); + conn->sess = session; + insque(&conn->clist, &session->conn_list); + } else { + log_error("Only a single connection supported " + "(initiator %s)", conn->initiator); + /* Fail the login */ + rsp->status_class = ISCSI_STATUS_INITIATOR_ERR; + rsp->status_detail = ISCSI_STATUS_TOO_MANY_CONN; + conn->state = STATE_EXIT; + res = -1; + goto out; + } } - /* add a new connection to the session */ - conn->session = session; } else { - if (req->sid.id.tsih) { - /* fail the login */ + if (req->sid.id.tsih != 0) { + log_error("Requested TSIH not 0 (TSIH %d, tid %d, " + "initiator %s, sid requisted %#" PRIx64 ")", + req->sid.id.tsih, conn->tid, conn->initiator, + req->sid.id64); + /* Fail the login */ rsp->status_class = ISCSI_STATUS_INITIATOR_ERR; rsp->status_detail = ISCSI_STATUS_SESSION_NOT_FOUND; conn->state = STATE_EXIT; - return; - } - /* instantiate a new session */ + res = -1; + goto out; + } else + log_debug(1, "New session sid %#" PRIx64 "(tid %d, " + "initiator %s)", req->sid.id64, + conn->tid, conn->initiator); } + +out: + return res; } static void text_scan_login(struct connection *conn) @@ -429,7 +467,16 @@ static void login_start(struct connection *conn) conn->state = STATE_EXIT; return; } + conn->initiator = strdup(name); + if (conn->initiator == NULL) { + log_error("Unable to dublicate initiator's name %s", name); + rsp->status_class = ISCSI_STATUS_TARGET_ERR; + rsp->status_detail = ISCSI_STATUS_NO_RESOURCES; + conn->state = STATE_EXIT; + return; + } + alias = text_key_find(conn, "InitiatorAlias"); session_type = text_key_find(conn, "SessionType"); target_name = text_key_find(conn, "TargetName"); @@ -465,34 +512,34 @@ static void login_start(struct connection *conn) return; } - if (ki->param_get(conn->tid, 0, key_session, - conn->session_param)) { - rsp->status_class = ISCSI_STATUS_TARGET_ERROR; - rsp->status_detail = ISCSI_STATUS_SVC_UNAVAILABLE; - conn->state = STATE_EXIT; - } + if (login_check_reinstatement(conn) != 0) + return; } + conn->exp_cmd_sn = be32_to_cpu(req->cmd_sn); - log_debug(1, "exp_cmd_sn: %d,%d", conn->exp_cmd_sn, req->cmd_sn); + log_debug(1, "exp_cmd_sn %u, cmd_sn %u", conn->exp_cmd_sn, req->cmd_sn); text_key_add(conn, "TargetPortalGroupTag", "1"); + return; } -static void login_finish(struct connection *conn) +static int login_finish(struct connection *conn) { + int res = 0; + switch (conn->session_type) { case SESSION_NORMAL: - { - - if (!conn->session) - session_create(conn); - conn->sid = conn->session->sid; + if (!conn->sess) + res = session_create(conn); + if (res == 0) + conn->sid = conn->sess->sid; break; - } case SESSION_DISCOVERY: /* set a dummy tsih value */ conn->sid.id.tsih = 1; break; } + + return res; } static void cmnd_reject(struct connection *conn, u8 reason) @@ -649,7 +696,6 @@ static void cmnd_exec_login(struct connection *conn) case STATE_SECURITY: case STATE_SECURITY_DONE: conn->state = STATE_SECURITY_LOGIN; - login_security_done(conn); break; default: goto init_err; @@ -665,7 +711,6 @@ static void cmnd_exec_login(struct connection *conn) break; } conn->state = STATE_SECURITY_FULL; - login_security_done(conn); break; case STATE_LOGIN: if (stay) @@ -677,8 +722,14 @@ static void cmnd_exec_login(struct connection *conn) goto init_err; } if (!stay && !nsg_disagree) { + int err; text_check_param(conn); - login_finish(conn); + err = login_finish(conn); + if (err != 0) { + log_debug(1, "login_finish() failed: %d", err); + /* Make initiator retry later */ + goto tgt_no_mem; + } } break; default: @@ -724,6 +775,13 @@ target_err: rsp->status_detail = ISCSI_STATUS_TARGET_ERROR; conn->state = STATE_EXIT; return; + +tgt_no_mem: + rsp->flags = 0; + rsp->status_class = ISCSI_STATUS_TARGET_ERR; + rsp->status_detail = ISCSI_STATUS_NO_RESOURCES; + conn->state = STATE_EXIT; + return; } static void text_scan_text(struct connection *conn) diff --git a/iscsi-scst/usr/iscsid.h b/iscsi-scst/usr/iscsid.h index 52d484f43..b96b95518 100644 --- a/iscsi-scst/usr/iscsid.h +++ b/iscsi-scst/usr/iscsid.h @@ -19,6 +19,7 @@ #include #include +#include #include "types.h" #include "iscsi_hdr.h" @@ -27,7 +28,8 @@ #include "config.h" #include "misc.h" -#define PROC_SESSION "/proc/scsi_tgt/iscsi/session" +#define sBUG() assert(0) +#define sBUG_ON(p) assert(!(p)) struct buf_segment { struct __qelem entry; @@ -56,7 +58,8 @@ struct session { struct target *target; union iscsi_sid sid; - int conn_cnt; + int kern_conn_cnt; + struct __qelem conn_list; }; struct connection { @@ -64,7 +67,7 @@ struct connection { int iostate; int fd; - struct session *session; + struct session *sess; u32 tid; struct iscsi_param session_param[session_key_last]; @@ -100,6 +103,8 @@ struct connection { unsigned char *challenge; } chap; } auth; + + struct __qelem clist; }; #define IOSTATE_FREE 0 @@ -138,6 +143,11 @@ struct connection { #define BHS_SIZE 48 +/* + * Must be 8192, since it used as MaxRecvDataSegmentLength during Login phase, + * because iSCSI RFC requires: "The default MaxRecvDataSegmentLength is used + * during Login". + */ #define INCOMING_BUFSIZE 8192 struct target { @@ -149,9 +159,6 @@ struct target { char name[ISCSI_NAME_LEN]; char *alias; - int max_nr_sessions; - int nr_sessions; - struct __qelem isns_head; }; @@ -165,8 +172,7 @@ extern int cmnd_exec_auth_chap(struct connection *conn); /* conn.c */ extern struct connection *conn_alloc(void); extern void conn_free(struct connection *conn); -extern int conn_test(struct connection *conn); -extern void conn_take_fd(struct connection *conn, int fd); +extern void conn_pass_to_kern(struct connection *conn, int fd); extern void conn_read_pdu(struct connection *conn); extern void conn_write_pdu(struct connection *conn); extern void conn_free_pdu(struct connection *conn); @@ -209,8 +215,9 @@ extern void __log_pdu(const char *func, int line, int level, struct PDU *pdu); /* session.c */ extern struct session *session_find_name(u32 tid, const char *iname, union iscsi_sid sid); extern struct session *session_find_id(u32 tid, u64 sid); -extern void session_create(struct connection *conn); -extern void session_remove(struct session *session); +extern int session_create(struct connection *conn); +extern void session_free(struct session *session); +extern struct connection *conn_find(struct session *session, u16 cid); /* target.c */ extern struct __qelem targets_list; @@ -225,23 +232,18 @@ extern int iscsi_adm_request_listen(void); extern int iscsi_adm_request_handle(int accept_fd); /* ctldev.c */ -struct iscsi_kernel_interface { - int (*ctldev_open) (int *); - int (*param_get) (u32, u64, int, struct iscsi_param *); - int (*param_set) (u32, u64, int, u32, struct iscsi_param *); - int (*target_create) (u32 *, char *); - int (*target_destroy) (u32); - int (*session_create) (u32, u64, u32, char *, char *); - int (*session_destroy) (u32, u64); - int (*conn_create) (u32, u64, u32, u32, u32, int, u32, u32); - int (*conn_destroy) (u32 tid, u64 sid, u32 cid); -}; - -extern struct iscsi_kernel_interface *ki; - -/* the following functions should be killed */ -extern int session_conns_close(u32 tid, u64 sid); -extern int target_destroy(u32 tid); +extern int kernel_open(int *max_data_seg_len); +extern int kernel_param_get(u32 tid, u64 sid, int type, struct iscsi_param *param); +extern int kernel_param_set(u32 tid, u64 sid, int type, u32 partial, + struct iscsi_param *param); +extern int kernel_target_create(u32 *tid, char *name); +extern int kernel_target_destroy(u32 tid); +extern int kernel_session_create(u32 tid, u64 sid, u32 exp_cmd_sn, + char *name, char *user); +extern int kernel_session_destroy(u32 tid, u64 sid); +extern int kernel_conn_create(u32 tid, u64 sid, u32 cid, u32 stat_sn, u32 exp_stat_sn, + int fd, u32 hdigest, u32 ddigest); +extern int kernel_conn_destroy(u32 tid, u64 sid, u32 cid); /* event.c */ extern void handle_iscsi_events(int fd); diff --git a/iscsi-scst/usr/message.c b/iscsi-scst/usr/message.c index 94b5e7d58..a26ead2d3 100644 --- a/iscsi-scst/usr/message.c +++ b/iscsi-scst/usr/message.c @@ -79,7 +79,7 @@ static void iscsi_adm_request_exec(struct iscsi_adm_req *req, struct iscsi_adm_r req->u.trgt.target_param); break; case C_TRGT_SHOW: - err = ki->param_get(req->tid, req->sid, key_target, + err = kernel_param_get(req->tid, req->sid, key_target, req->u.trgt.target_param); break; @@ -88,14 +88,14 @@ static void iscsi_adm_request_exec(struct iscsi_adm_req *req, struct iscsi_adm_r case C_SESS_UPDATE: break; case C_SESS_SHOW: - err = ki->param_get(req->tid, req->sid, key_session, + err = kernel_param_get(req->tid, req->sid, key_session, req->u.trgt.session_param); break; case C_CONN_NEW: case C_CONN_DEL: conn_blocked = 1; - err = ki->conn_destroy(req->tid, req->sid, req->cid); + err = kernel_conn_destroy(req->tid, req->sid, req->cid); conn_blocked = 0; break; case C_CONN_UPDATE: diff --git a/iscsi-scst/usr/param.c b/iscsi-scst/usr/param.c index b677ccfac..cad5bf81a 100644 --- a/iscsi-scst/usr/param.c +++ b/iscsi-scst/usr/param.c @@ -280,11 +280,19 @@ static struct iscsi_key_ops marker_ops = { #define SET_KEY_VALUES(x) DEFAULT_NR_##x,DEFAULT_NR_##x,MIN_NR_##x,MAX_NR_##x +/* + * List of local target keys with initial values. + * Must match corresponding key_* enum in iscsi_scst.h!! + */ struct iscsi_key target_keys[] = { {"QueuedCommands", SET_KEY_VALUES(QUEUED_CMNDS), &minimum_ops}, {NULL,}, }; +/* + * List of iSCSI RFC specified session keys with initial values. + * Must match corresponding key_* enum in iscsi_scst.h!! + */ struct iscsi_key session_keys[] = { {"InitialR2T", 1, 0, 0, 1, &or_ops}, {"ImmediateData", 1, 1, 0, 1, &and_ops}, diff --git a/iscsi-scst/usr/plain.c b/iscsi-scst/usr/plain.c index 977cfefad..20efe3f78 100644 --- a/iscsi-scst/usr/plain.c +++ b/iscsi-scst/usr/plain.c @@ -324,7 +324,7 @@ static int netmask_match_v4(struct sockaddr *sa1, struct sockaddr *sa2, uint32_t static int netmask_match(struct sockaddr *sa1, struct sockaddr *sa2, char *buf) { - uint32_t mbit; + int32_t mbit; uint8_t family = sa1->sa_family; mbit = strtoul(buf, NULL, 0); @@ -465,7 +465,6 @@ static int plain_initiator_access(u32 tid, int fd) static int __plain_target_create(u32 *tid, char *name, int update) { int err; - struct iscsi_param params[session_key_last]; if (target_find_by_name(name)) { log_error("duplicated target %s", name); @@ -474,13 +473,6 @@ static int __plain_target_create(u32 *tid, char *name, int update) if ((err = target_add(tid, name)) < 0) return err; - param_set_defaults(params, session_keys); - if ((err = ki->param_set(*tid, 0, key_session, 0, params)) < 0) - return err; - - if (update) - ; /* Update the config file here. */ - return err; } @@ -505,12 +497,9 @@ static int __plain_param_set(u32 tid, u64 sid, int type, { int err; - if ((err = ki->param_set(tid, sid, type, partial, param)) < 0) + if ((err = kernel_param_set(tid, sid, type, partial, param)) < 0) return err; - if (update) - ; - return err; } diff --git a/iscsi-scst/usr/session.c b/iscsi-scst/usr/session.c index 64c0b45f2..3567eac4d 100644 --- a/iscsi-scst/usr/session.c +++ b/iscsi-scst/usr/session.c @@ -26,22 +26,33 @@ #include "iscsid.h" -static struct session *session_alloc(u32 tid) +static int session_alloc(u32 tid, struct session **psess) { struct session *session; struct target *target = target_find_by_id(tid); + int res = 0; + + if (!target) { + log_error("tid %x not found", tid); + res = -ENOENT; + goto out; + } + + if (!(session = malloc(sizeof(*session)))) { + res = -ENOMEM; + goto out; + } - if (!target) - return NULL; - if (!(session = malloc(sizeof(*session)))) - return NULL; memset(session, 0, sizeof(*session)); session->target = target; INIT_LIST_HEAD(&session->slist); insque(&session->slist, &target->sessions_list); - return session; + *psess = session; + +out: + return res; } struct session *session_find_name(u32 tid, const char *iname, union iscsi_sid sid) @@ -49,10 +60,13 @@ struct session *session_find_name(u32 tid, const char *iname, union iscsi_sid si struct session *session; struct target *target; - if (!(target = target_find_by_id(tid))) + if (!(target = target_find_by_id(tid))) { + log_error("Target tid %d not found", tid); return NULL; + } + + log_debug(1, "Finding session %s, sid %#" PRIx64, iname, sid.id64); - log_debug(1, "session_find_name: %s %#" PRIx64, iname, sid.id64); list_for_each_entry(session, &target->sessions_list, slist) { if (!memcmp(sid.id.isid, session->sid.id.isid, 6) && !strcmp(iname, session->initiator)) @@ -70,7 +84,8 @@ struct session *session_find_id(u32 tid, u64 sid) if (!(target = target_find_by_id(tid))) return NULL; - log_debug(1, "session_find_id: %#" PRIx64, sid); + log_debug(1, "Searching for sid %#" PRIx64, sid); + list_for_each_entry(session, &target->sessions_list, slist) { if (session->sid.id64 == sid) return session; @@ -79,105 +94,101 @@ struct session *session_find_id(u32 tid, u64 sid) return NULL; } -static int session_test(u32 t_tid, u64 t_sid) +int session_create(struct connection *conn) { - FILE *f; - char buf[8192], *p; - u32 tid; - u64 sid; - int err = -ENOENT, find = 0; - - if ((f = fopen(PROC_SESSION, "r")) == NULL) { - fprintf(stderr, "Can't open %s\n", PROC_SESSION); - return -errno; - } - - while (fgets(buf, sizeof(buf), f)) { - p = buf; - while (isspace((int) *p)) - p++; - - if (!strncmp(p, "tid:", 4)) { - if (sscanf(p, "tid:%u", &tid) != 1) { - err = -EIO; - goto out; - } - if (tid == t_tid) - find = 1; - else - find = 0; - } else if (!strncmp(p, "sid:", 4)) { - if (!find) - continue; - if (sscanf(p, "sid:%" SCNu64, &sid) != 1) { - err = -EIO; - goto out; - } - - if (sid == t_sid) { - err = 0; - goto out; - } - } - } - -out: - fclose(f); - - return err; -} - -void session_create(struct connection *conn) -{ - struct session *session; + /* We are single threaded, so it desn't need any protection */ static u16 tsih = 1; + struct session *session; char *user; + int res = 0; - if (!(session = session_alloc(conn->tid))) - return; + res = session_alloc(conn->tid, &session); + if (res != 0) { + log_error("session_alloc() failed: %d", res); + goto out; + } session->sid = conn->sid; session->sid.id.tsih = tsih; + INIT_LIST_HEAD(&session->conn_list); + + insque(&conn->clist, &session->conn_list); + conn->sess = session; + + conn->sess->initiator = strdup(conn->initiator); + if (conn->sess->initiator == NULL) { + res = -errno; + log_error("strdup() failed: %d", res); + goto out_free; + } while (1) { - int err = session_test(conn->tid, session->sid.id64); + struct session *s; - if (err == -ENOENT) + s = session_find_id(conn->tid, session->sid.id64); + if (s != NULL) break; - else if (err < 0) - return; + + log_debug(1, "tsih %x already exists", session->sid.id.tsih); session->sid.id.tsih++; } tsih = session->sid.id.tsih + 1; - conn->session = session; - conn->session->initiator = strdup(conn->initiator); - - log_debug(1, "session_create: %#" PRIx64, session->sid.id64); + log_debug(1, "sid %#" PRIx64, session->sid.id64); if (conn->user != NULL) user = conn->user; else user = ""; - ki->session_create(conn->tid, session->sid.id64, conn->exp_cmd_sn, - session->initiator, user); - ki->param_set(conn->tid, session->sid.id64, key_session, 0, + res = kernel_session_create(conn->tid, session->sid.id64, conn->exp_cmd_sn, + session->initiator, user); + if (res != 0) { + log_error("kernel_session_create() failed: %d", res); + goto out_free; + } + + res = kernel_param_set(conn->tid, session->sid.id64, key_session, 0, conn->session_param); + if (res != 0) { + log_error("kernel_param_set() failed: %d", res); + goto out_destroy; + } + +out: + return res; + +out_destroy: + kernel_session_destroy(conn->tid, session->sid.id64); + +out_free: + session_free(session); + conn->sess = NULL; + goto out; } -void session_remove(struct session *session) +void session_free(struct session *session) { - log_debug(1, "session_remove: %#" PRIx64, session->sid.id64); + log_debug(1, "Freeing session sid %#"PRIx64, session->sid.id64); if (!session->sid.id.tsih) - ki->session_destroy(session->target->tid, session->sid.id64); + kernel_session_destroy(session->target->tid, session->sid.id64); - if (session->target) { + if (session->target) remque(&session->slist); -/* session->target->nr_sessions--; */ - } free(session->initiator); free(session); } + +struct connection *conn_find(struct session *session, u16 cid) +{ + struct connection *conn; + + list_for_each_entry(conn, &session->conn_list, clist) { + if (conn->cid == cid) + return conn; + } + + return NULL; +} diff --git a/iscsi-scst/usr/target.c b/iscsi-scst/usr/target.c index 7c540a2c2..2c31ae4d4 100644 --- a/iscsi-scst/usr/target.c +++ b/iscsi-scst/usr/target.c @@ -82,7 +82,7 @@ static void all_accounts_del(u32 tid, int dir) int target_del(u32 tid) { struct target *target = target_find_by_id(tid); - int err = ki->target_destroy(tid); + int err = kernel_target_destroy(tid); if (err < 0 && errno != ENOENT) return -errno; @@ -114,6 +114,7 @@ int target_add(u32 *tid, char *name) { struct target *target; int err; + struct iscsi_param params[target_key_last]; if (!name) return -EINVAL; @@ -124,9 +125,16 @@ int target_add(u32 *tid, char *name) memset(target, 0, sizeof(*target)); memcpy(target->name, name, sizeof(target->name) - 1); - if ((err = ki->target_create(tid, name)) < 0) { + if ((err = kernel_target_create(tid, name)) < 0) { log_warning("can't create a target %d %u\n", errno, *tid); - goto out; + goto out_free; + } + + param_set_defaults(params, target_keys); + err = kernel_param_set(*tid, 0, key_target, 0, params); + if (err != 0) { + log_error("kernel_param_set() failed: %s", strerror(errno)); + goto out_destroy; } INIT_LIST_HEAD(&target->tlist); @@ -137,8 +145,13 @@ int target_add(u32 *tid, char *name) isns_target_register(name); - return 0; out: - free(target); return err; + +out_destroy: + kernel_target_destroy(*tid); + +out_free: + free(target); + goto out; }