# 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
#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;
#define DIGEST_NONE (1 << 0)
#define DIGEST_CRC32C (1 << 1)
-struct conn_info {
+struct iscsi_kern_conn_info {
u32 tid;
aligned_u64 sid;
key_target,
};
-struct iscsi_param_info {
+struct iscsi_kern_param_info {
u32 tid;
aligned_u64 sid;
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;
};
#define NETLINK_ISCSI_SCST 25
/* On success returns MAX_DATA_SEG_LEN */
-#define REGISTER_USERD _IOW('s', 0, struct iscsi_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 REGISTER_USERD _IOW('s', 0, struct iscsi_kern_register_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)
{
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)
{
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)
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)
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)
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)
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)
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;
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:
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;
err = del_conn(target, (void __user *) arg);
break;
- case GET_CONN_INFO:
- err = get_conn_info(target, (void __user *) arg);
- break;
-
default:
sBUG();
break;
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;
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)
{
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;
}
{
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,
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)
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
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;
#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;
}
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);
/* 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;
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;
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;
}
/* 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;
+ }
+
+ err = iscsi_conn_alloc(session, info, reinstatement, &new_conn);
+ if (err != 0)
+ goto out;
- return iscsi_conn_alloc(session, info);
+ 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;
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;
}
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;
}
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;
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;
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;
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;
"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);
iscsi_cmnd_exec(cmnd);
+ spin_lock(&session->sn_lock);
+
if (list_empty(&session->pending_list))
break;
cmnd = list_entry(session->pending_list.next,
TRACE_DBG("Processing pending cmd %p (cmd_sn %u)",
cmnd, cmd_sn);
-
- spin_lock(&session->sn_lock);
}
} else {
int drop = 0;
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,
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;
}
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;
}
#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,
};
kfree(thr);
goto out;
}
- list_add(&thr->threads_list_entry, &iscsi_threads_list);
+ list_add_tail(&thr->threads_list_entry, &iscsi_threads_list);
}
out:
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 */
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;
};
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;
};
/* 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
#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;
/* 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);
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;
}
} while (req_freed);
spin_unlock(&session->sn_lock);
+
return;
}
}
} while (req_freed);
spin_unlock(&session->sn_lock);
+
return;
}
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)
{
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)
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;
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);
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();
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);
}
/* 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;
/* 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;
}
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;
}
/* 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;
/* 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;
/* 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;
/* 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;
/* 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;
}
/* 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;
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;
/* 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;
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);
}
/* 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;
}
{
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)) {
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);
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;
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)
{
}
/* 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;
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);
goto out_free;
}
- list_add(&target->target_list_entry, &target_list);
+ list_add_tail(&target->target_list_entry, &target_list);
return 0;
}
/* 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;
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,
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;
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);
}
}
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);
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
{
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;
}
conn->iostate = IOSTATE_READ_BHS;
conn->buffer = (void *)&conn->req.bhs;
conn->rwsize = BHS_SIZE;
+ return;
}
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)
conn->rsp.datasize = 0;
conn->rsp.data = NULL;
+ return;
}
void conn_free_pdu(struct connection *conn)
conn->rsp.ahs = NULL;
}
conn_free_rsp_buf_list(conn);
+ return;
}
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];
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");
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));
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));
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;
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;
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;
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));
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));
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));
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;
void handle_iscsi_events(int fd)
{
struct session *session;
- struct iscsi_event event;
+ struct iscsi_kern_event event;
int res;
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);
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++) {
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);
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)
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:
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);
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;
}
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);
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);
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.
+ */
+
+ sBUG_ON(conn->sess != NULL);
- 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;
+ 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)
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");
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)
case STATE_SECURITY:
case STATE_SECURITY_DONE:
conn->state = STATE_SECURITY_LOGIN;
- login_security_done(conn);
break;
default:
goto init_err;
break;
}
conn->state = STATE_SECURITY_FULL;
- login_security_done(conn);
break;
case STATE_LOGIN:
if (stay)
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:
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)
#include <search.h>
#include <sys/types.h>
+#include <assert.h>
#include "types.h"
#include "iscsi_hdr.h"
#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;
struct target *target;
union iscsi_sid sid;
- int conn_cnt;
+ int kern_conn_cnt;
+ struct __qelem conn_list;
};
struct connection {
int iostate;
int fd;
- struct session *session;
+ struct session *sess;
u32 tid;
struct iscsi_param session_param[session_key_last];
unsigned char *challenge;
} chap;
} auth;
+
+ struct __qelem clist;
};
#define IOSTATE_FREE 0
#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 {
char name[ISCSI_NAME_LEN];
char *alias;
- int max_nr_sessions;
- int nr_sessions;
-
struct __qelem isns_head;
};
/* 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);
/* 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;
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);
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;
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:
#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},
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);
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);
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;
}
{
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;
}
#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)
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))
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;
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;
+}
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;
{
struct target *target;
int err;
+ struct iscsi_param params[target_key_last];
if (!name)
return -EINVAL;
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);
isns_target_register(name);
- return 0;
out:
- free(target);
return err;
+
+out_destroy:
+ kernel_target_destroy(*tid);
+
+out_free:
+ free(target);
+ goto out;
}