mirror of
https://github.com/SCST-project/scst.git
synced 2026-05-18 11:11:27 +00:00
- Fixed a major IET-derived iSCSI RFC violation: sessions and connections were not working
- A lot of cleanups and minor fixes, mostly IET-derived git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@685 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <search.h>
|
||||
#include <sys/types.h>
|
||||
#include <assert.h>
|
||||
|
||||
#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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user