diff options
-rw-r--r-- | TODO | 4 | ||||
-rw-r--r-- | include/iscsi_u.h | 85 | ||||
-rw-r--r-- | kernel/iscsi_if.h | 22 | ||||
-rw-r--r-- | kernel/iscsi_tcp.c | 45 | ||||
-rw-r--r-- | kernel/iscsi_tcp.h | 1 | ||||
-rw-r--r-- | kernel/iscsi_u.c | 389 | ||||
-rw-r--r-- | usr/ctldev.c | 59 | ||||
-rw-r--r-- | usr/discovery.c | 10 | ||||
-rw-r--r-- | usr/idbm.c | 2 | ||||
-rw-r--r-- | usr/idbm.h | 6 | ||||
-rw-r--r-- | usr/initiator.c | 361 | ||||
-rw-r--r-- | usr/initiator.h | 37 | ||||
-rw-r--r-- | usr/io.c | 60 | ||||
-rw-r--r-- | usr/login.c | 60 |
14 files changed, 793 insertions, 348 deletions
@@ -31,3 +31,7 @@ fixme: * support residual_count in case of scatter-gather Data-In. As of today, only non-sg Scsi_Cmnd's are working. But I can amazing some target which will send residual_count in the middle of scatter-gather Data-In. + +* daemon should manage its own ITT-space +* send_pdu() should use new "anon" poll of mtasks. the anon-mtask will be freed + right after its xmited diff --git a/include/iscsi_u.h b/include/iscsi_u.h index a160248..9a327fd 100644 --- a/include/iscsi_u.h +++ b/include/iscsi_u.h @@ -29,9 +29,27 @@ #define UEVENT_BASE 10 #define KEVENT_BASE 100 -/* up events */ +typedef enum { + ISCSI_PARAM_MAX_RECV_DLENGTH = 0, + ISCSI_PARAM_MAX_XMIT_DLENGTH = 1, + ISCSI_PARAM_HDRDGST_EN = 2, + ISCSI_PARAM_DATADGST_EN = 3, + ISCSI_PARAM_INITIAL_R2T_EN = 4, + ISCSI_PARAM_MAX_R2T = 5, + ISCSI_PARAM_IMM_DATA_EN = 6, + ISCSI_PARAM_FIRST_BURST = 7, + ISCSI_PARAM_MAX_BURST = 8, + ISCSI_PARAM_PDU_INORDER_EN = 9, + ISCSI_PARAM_DATASEQ_INORDER_EN = 10, + ISCSI_PARAM_ERL = 11, + ISCSI_PARAM_IFMARKER_EN = 12, + ISCSI_PARAM_OFMARKER_EN = 13, +} iscsi_param_e; + typedef enum iscsi_uevent_e { ISCSI_UEVENT_UNKNOWN = 0, + + /* down events */ ISCSI_UEVENT_CREATE_SESSION = UEVENT_BASE + 1, ISCSI_UEVENT_DESTROY_SESSION = UEVENT_BASE + 2, ISCSI_UEVENT_CREATE_CNX = UEVENT_BASE + 3, @@ -39,14 +57,16 @@ typedef enum iscsi_uevent_e { ISCSI_UEVENT_BIND_CNX = UEVENT_BASE + 5, ISCSI_UEVENT_SEND_PDU_BEGIN = UEVENT_BASE + 6, ISCSI_UEVENT_SEND_PDU_END = UEVENT_BASE + 7, -} iscsi_uevent_e; + ISCSI_UEVENT_RECV_PDU_BEGIN = UEVENT_BASE + 8, + ISCSI_UEVENT_RECV_PDU_END = UEVENT_BASE + 9, + ISCSI_UEVENT_RECV_REQ = UEVENT_BASE + 10, + ISCSI_UEVENT_SET_PARAM = UEVENT_BASE + 11, + ISCSI_UEVENT_START_CNX = UEVENT_BASE + 12, -/* down events */ -typedef enum iscsi_kevent_e { - ISCSI_KEVENT_UNKNOWN = 0, - ISCSI_KEVENT_CNX_ERROR = KEVENT_BASE + 1, - ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 2, -} iscsi_kevent_e; + /* up events */ + ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1, + ISCSI_KEVENT_CNX_ERROR = KEVENT_BASE + 2, +} iscsi_uevent_e; typedef struct iscsi_uevent { int type; /* k/u events type */ @@ -55,7 +75,7 @@ typedef struct iscsi_uevent { union { /* messages u -> k */ struct msg_create_session { - ulong_t handle; + ulong_t session_handle; unsigned int sid; unsigned int initial_cmdsn; } c_session; @@ -64,7 +84,7 @@ typedef struct iscsi_uevent { } d_session; struct msg_create_cnx { ulong_t session_handle; - ulong_t handle; + ulong_t cnx_handle; int socket_fd; unsigned int cid; } c_cnx; @@ -84,32 +104,39 @@ typedef struct iscsi_uevent { struct msg_sp_end { ulong_t cnx_handle; } sp_end; + struct msg_rp_begin { + ulong_t cpcnx_handle; + ulong_t recv_handle; + } rp_begin; + struct msg_rp_end_req { + ulong_t cpcnx_handle; + ulong_t pdu_handle; + } rp_end; + struct msg_set_param { + ulong_t cnx_handle; + iscsi_param_e param; + unsigned int value; + } set_param; + struct msg_start_cnx { + ulong_t cnx_handle; + } start_cnx; } u; union { - /* results */ + /* messages k -> u */ ulong_t handle; int retcode; - } r; -} iscsi_uevent_t; - -typedef struct iscsi_kevent { - int type; /* k/u events type */ - - union { - /* messages k -> u */ + struct msg_recv_req { + ulong_t recv_handle; + ulong_t cnx_handle; + } recv_req; struct msg_cnx_error { unsigned int cid; } cnxerror; - struct msg_recv_pdu { - unsigned int cid; - unsigned int pdulen; - } recvpdu; - } u; - union { - /* results */ - ulong_t handle; - int retcode; + struct msg_rp_begin_rsp { + ulong_t pdu_handle; + unsigned int pdu_size; + } rp_begin; } r; -} iscsi_kevent_t; +} iscsi_uevent_t; #endif /* ISCSI_U_H */ diff --git a/kernel/iscsi_if.h b/kernel/iscsi_if.h index 4730250..9aa471d 100644 --- a/kernel/iscsi_if.h +++ b/kernel/iscsi_if.h @@ -21,10 +21,10 @@ #include <net/tcp.h> #include <iscsi_proto.h> +#include <iscsi_u.h> typedef void* iscsi_snx_h; /* iSCSI Data-Path session handle */ typedef void* iscsi_cnx_h; /* iSCSI Data-Path connection handle */ -typedef void* iscsi_pdu_h; /* iSCSI Control-Path PDU handle */ typedef enum { ISCSI_STATE_FREE = 1, @@ -32,23 +32,6 @@ typedef enum { ISCSI_STATE_FAILED = 3, } iscsi_session_state_e; -typedef enum { - ISCSI_PARAM_MAX_RECV_DLENGH = 0, - ISCSI_PARAM_MAX_XMIT_DLENGH = 1, - ISCSI_PARAM_HDRDGST_EN = 2, - ISCSI_PARAM_DATADGST_EN = 3, - ISCSI_PARAM_INITIAL_R2T_EN = 4, - ISCSI_PARAM_MAX_R2T = 5, - ISCSI_PARAM_IMM_DATA_EN = 6, - ISCSI_PARAM_FIRST_BURST = 7, - ISCSI_PARAM_MAX_BURST = 8, - ISCSI_PARAM_PDU_INORDER_EN = 9, - ISCSI_PARAM_DATASEQ_INORDER_EN = 10, - ISCSI_PARAM_ERL = 11, - ISCSI_PARAM_IFMARKER_EN = 12, - ISCSI_PARAM_OFMARKER_EN = 13, -} iscsi_param_e; - #define ISCSI_CTRL_ERR_BASE 100 #define ISCSI_DP_ERR_BASE 1000 @@ -139,7 +122,8 @@ typedef struct iscsi_ops { int data_size); } iscsi_ops_t; -int iscsi_control_recv_pdu(iscsi_cnx_h cp_cnx, iscsi_hdr_t *hdr, char *data); +int iscsi_control_recv_pdu(iscsi_cnx_h cp_cnx, iscsi_hdr_t *hdr, + char *data, int data_size); void iscsi_control_cnx_error(iscsi_cnx_h cp_cnx, int error); /* FIXME: generic register/unregister interface needed */ diff --git a/kernel/iscsi_tcp.c b/kernel/iscsi_tcp.c index 33abb04..119a013 100644 --- a/kernel/iscsi_tcp.c +++ b/kernel/iscsi_tcp.c @@ -997,7 +997,6 @@ iscsi_hdr_recv(iscsi_conn_t *conn) * placeholder */ memcpy(&conn->hdr, conn->in.hdr, sizeof(iscsi_hdr_t)); - conn->data_copied = 0; } } else if (cstate == IN_PROGRESS_WRITE) { rc = iscsi_cmd_rsp(conn, ctask); @@ -1024,15 +1023,13 @@ iscsi_hdr_recv(iscsi_conn_t *conn) iscsi_mgmt_task_t *mtask; rc = iscsi_control_recv_pdu( - conn->handle, hdr, NULL); + conn->handle, hdr, NULL, 0); mtask = (iscsi_mgmt_task_t *) session->imm_cmds[conn->in.itt - ISCSI_IMM_ITT_OFFSET]; if (conn->c_stage == ISCSI_CNX_STARTED) { iscsi_enqueue(&session->immpool, mtask); } - } else { - conn->data_copied = 0; } break; default: @@ -1047,19 +1044,18 @@ iscsi_hdr_recv(iscsi_conn_t *conn) ISCSI_IMM_ITT_OFFSET]; debug_scsi("immrsp [op 0x%x cid %d itt 0x%x len %d]\n", - hdr->opcode, conn->id, mtask->itt, conn->in.datalen); + conn->in.opcode, conn->id, mtask->itt, + conn->in.datalen); switch(conn->in.opcode) { case ISCSI_OP_LOGIN_RSP: case ISCSI_OP_TEXT_RSP: if (!conn->in.datalen) { rc = iscsi_control_recv_pdu( - conn->handle, hdr, NULL); + conn->handle, hdr, NULL, 0); if (conn->c_stage == ISCSI_CNX_STARTED) { iscsi_enqueue(&session->immpool, mtask); } - } else { - conn->data_copied = 0; } break; default: @@ -1067,11 +1063,10 @@ iscsi_hdr_recv(iscsi_conn_t *conn) break; } } else if (conn->in.itt == ISCSI_RESERVED_TAG) { - conn->data_copied = 0; if (conn->in.opcode == ISCSI_OP_NOOP_IN && !conn->in.datalen) { rc = iscsi_control_recv_pdu( - conn->handle, hdr, NULL); + conn->handle, hdr, NULL, 0); } else { rc = ISCSI_ERR_BAD_OPCODE; } @@ -1187,12 +1182,13 @@ iscsi_data_recv(iscsi_conn_t *conn) } rc = iscsi_control_recv_pdu(conn->handle, - conn->in.hdr, conn->data); + conn->in.hdr, conn->data, conn->in.datalen); if (mtask && conn->c_stage == ISCSI_CNX_STARTED) { iscsi_enqueue(&session->immpool, mtask); } } + break; default: __BUG_ON(1); } @@ -1450,6 +1446,7 @@ iscsi_mtask_xmit(iscsi_conn_t *conn, iscsi_mgmt_task_t *mtask) } if (mtask->data_count == 0 && mtask->hdr.itt == ISCSI_RESERVED_TAG) { + up(&mtask->xmitsema); return 0; } else if (mtask->data_count) { mtask->in_progress = IN_PROGRESS_IMM_DATA; @@ -1469,6 +1466,7 @@ iscsi_mtask_xmit(iscsi_conn_t *conn, iscsi_mgmt_task_t *mtask) } while (mtask->data_count); } + up(&mtask->xmitsema); return 0; } @@ -1708,9 +1706,9 @@ iscsi_data_xmit(iscsi_conn_t *conn) /* process immediate queue */ while ((mtask = __dequeue(&conn->immqueue)) != NULL) { - iscsi_session_t *session = conn->session; +// iscsi_session_t *session = conn->session; - __enqueue(&session->immpool, mtask); +// __enqueue(&session->immpool, mtask); if (conn->c_stage == ISCSI_CNX_STOPPED) { __insert(&conn->immqueue, mtask); spin_unlock_bh(&conn->lock); @@ -1893,12 +1891,12 @@ iscsi_eh_abort(struct scsi_cmnd *sc) debug_scsi("abort [sc %lx itt 0x%x]\n", (long)sc, ctask->itt); if (iscsi_control_recv_pdu(conn->handle, - (iscsi_hdr_t*)&ctask->hdr, NULL)) { + (iscsi_hdr_t*)&ctask->hdr, NULL, 0)) { return FAILED; } #if 0 - iscsi_session_t *session = conn->session; iscsi_mgmt_task_t *mtask; + iscsi_session_t *session = conn->session; debug_scsi("abort [sc %lx itt 0x%x]\n", (long)sc, ctask->itt); @@ -1915,7 +1913,7 @@ iscsi_eh_abort(struct scsi_cmnd *sc) iscsi_enqueue(&conn->immqueue, mtask); schedule_work(&conn->xmitwork); - down(&session->tmsema); + down(&mtask->xmitsema); #endif return SUCCESS; } @@ -2057,6 +2055,10 @@ iscsi_conn_create(iscsi_snx_h snxh, iscsi_cnx_h handle, conn->exp_statsn = 0; conn->handle = handle; + /* some initial operational parameters */ + conn->hdr_size = sizeof(iscsi_hdr_t); + conn->max_recv_dlength = DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH; + spin_lock_init(&conn->lock); /* initialize xmit PDU commands queue */ @@ -2253,11 +2255,10 @@ iscsi_send_immpdu(iscsi_cnx_h cnxh, iscsi_hdr_t *hdr, char *data, sizeof(iscsi_hdr_t)); if (mtask->data_count) { - iscsi_buf_init_virt(&mtask->sendbuf, (char*)&mtask->data, + iscsi_buf_init_virt(&mtask->sendbuf, (char*)mtask->data, mtask->data_count); /* FIXME: implement convertion of mtask->data into 1st - * mtask->sendbuf. - * Keep in mind that virtual buffer + * mtask->sendbuf. Keep in mind that virtual buffer * spreaded accross multiple pages... */ if(mtask->sendbuf.offset + mtask->data_count > PAGE_SIZE) { if (conn->c_stage == ISCSI_CNX_STARTED) { @@ -2269,6 +2270,7 @@ iscsi_send_immpdu(iscsi_cnx_h cnxh, iscsi_hdr_t *hdr, char *data, iscsi_enqueue(&conn->immqueue, mtask); schedule_work(&conn->xmitwork); + down(&mtask->xmitsema); return 0; } @@ -2423,6 +2425,7 @@ iscsi_session_create(iscsi_snx_h handle, int host_no, int initial_cmdsn) /* pre-format immediate cmds pool with ITT */ for (cmd_i=0; cmd_i<session->imm_max; cmd_i++) { session->imm_cmds[cmd_i]->itt = ISCSI_IMM_ITT_OFFSET + cmd_i; + init_MUTEX_LOCKED(&session->imm_cmds[cmd_i]->xmitsema); } if (iscsi_r2tpool_alloc(session)) { @@ -2480,10 +2483,10 @@ iscsi_set_param(iscsi_cnx_h cnxh, iscsi_param_e param, int value) if (conn->c_stage == ISCSI_CNX_INITIAL_STAGE) { switch(param) { - case ISCSI_PARAM_MAX_RECV_DLENGH: + case ISCSI_PARAM_MAX_RECV_DLENGTH: conn->max_recv_dlength = value; break; - case ISCSI_PARAM_MAX_XMIT_DLENGH: + case ISCSI_PARAM_MAX_XMIT_DLENGTH: conn->max_xmit_dlength = value; break; case ISCSI_PARAM_HDRDGST_EN: diff --git a/kernel/iscsi_tcp.h b/kernel/iscsi_tcp.h index f0939cb..5324ace 100644 --- a/kernel/iscsi_tcp.h +++ b/kernel/iscsi_tcp.h @@ -233,6 +233,7 @@ typedef struct iscsi_mgmt_task { iscsi_buf_t sendbuf; /* in progress buffer */ int sent; uint32_t itt; /* this ITT */ + struct semaphore xmitsema; } iscsi_mgmt_task_t; typedef union iscsi_union_task { diff --git a/kernel/iscsi_u.c b/kernel/iscsi_u.c index 1a89248..f6780f7 100644 --- a/kernel/iscsi_u.c +++ b/kernel/iscsi_u.c @@ -24,123 +24,127 @@ #include <linux/poll.h> #include <asm/uaccess.h> -/* Must go: for scsi_transport... */ -#include <scsi/scsi_host.h> - #include "iscsi_proto.h" #include "iscsi_if.h" #include "iscsi_u.h" +#define CTRL_RECV_ALLOWED 16 + typedef struct iscsi_kprovider { char name[ISCSI_PROVIDER_NAME_MAXLEN]; iscsi_ops_t ops; iscsi_caps_t caps; } iscsi_kprovider_t; +static iscsi_kprovider_t provider_table[ISCSI_PROVIDER_MAX]; -typedef enum sp_state_e { - SP_STATE_INVALID = 0, - SP_STATE_BUSY = 1, - SP_STATE_READY = 2, -} sp_state_e; - -typedef struct sp_item { - struct list_head item; - sp_state_e state; - char pdu[DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH+sizeof(iscsi_hdr_t)+4]; -} sp_item_t; +typedef enum pdu_state_e { + PDU_STATE_INVALID = 0, + PDU_STATE_READY = 1, + PDU_STATE_BUSY = 2, +} pdu_state_e; -typedef struct sp_context { +typedef struct xmit_context { ulong_t cnxh; - sp_state_e state; - sp_item_t *spb; + pdu_state_e state; + char *pdu; int hdr_size; int data_size; int curr_off; -} sp_context_t; - -static struct list_head sp_head; -static sp_context_t sp_ctxt; -static iscsi_kprovider_t provider_table[ISCSI_PROVIDER_MAX]; -static spinlock_t event_queue_lock = SPIN_LOCK_UNLOCKED; -static LIST_HEAD(event_queue); -DECLARE_WAIT_QUEUE_HEAD(event_wait); - -typedef struct kevent { - iscsi_uevent_t ev; - struct list_head list; -} kevent_t; - -int -iscsi_control_recv_pdu(iscsi_cnx_h cp_cnx, iscsi_hdr_t *hdr, char *data) -{ - BUG_ON(1); - return 0; -} - -void -iscsi_control_cnx_error(iscsi_cnx_h cp_cnx, int error) -{ -} +} xmit_context_t; -kevent_t* -iscsi_event_get(int del) +typedef struct recv_context { + struct list_head item; + iscsi_uevent_e type; + ulong_t cp_cnxh; + char *pdu; + int pdu_size; + pdu_state_e state; + int curr_off; +} recv_context_t; + +static xmit_context_t xmit; +static recv_context_t *recv = NULL; +static struct list_head evqueue; +static struct list_head evqueue_busy; +static spinlock_t evqueue_lock; +static int recv_entry_cnt = 0; +DECLARE_WAIT_QUEUE_HEAD(evwait); + +static recv_context_t* +recv_entry_get(int del) { - kevent_t *kevent = ERR_PTR(-EAGAIN); + recv_context_t *entry = ERR_PTR(-EAGAIN); - spin_lock(&event_queue_lock); - if (list_empty(&event_queue)) + spin_lock_bh(&evqueue_lock); + if (list_empty(&evqueue)) goto out; - kevent = list_entry(event_queue.next, kevent_t, list); - if (del) - list_del(&kevent->list); + entry = list_entry(evqueue.next, recv_context_t, item); + if (del) { + list_del(&entry->item); + recv_entry_cnt--; + } out: - spin_unlock(&event_queue_lock); + spin_unlock_bh(&evqueue_lock); - return kevent; + return entry; } int -iscsi_event_put(iscsi_uevent_e type, int atomic) +iscsi_control_recv_pdu(iscsi_cnx_h cp_cnx, iscsi_hdr_t *hdr, + char *data, int data_size) { - kevent_t *kevent; + recv_context_t *entry; - if (atomic) { - kevent = kmalloc(sizeof(*kevent), GFP_ATOMIC); - if (!kevent) - return -ENOMEM; - } else { - do { - kevent = kmalloc(sizeof(*kevent), GFP_KERNEL); - if (!kevent) - yield(); - } while (!kevent); - } - - memset(kevent, 0, sizeof(*kevent)); - INIT_LIST_HEAD(&kevent->list); + if (recv_entry_cnt >= CTRL_RECV_ALLOWED) + return -EPERM; - kevent->ev.type = type; + entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + if (!entry) + return -ENOMEM; + memset(entry, 0, sizeof(*entry)); - spin_lock(&event_queue_lock); - list_add(&kevent->list, &event_queue); - spin_unlock(&event_queue_lock); + entry->pdu = kmalloc(data_size + sizeof(iscsi_hdr_t), GFP_KERNEL); + if (!entry->pdu) { + kfree(entry); + return -ENOMEM; + } + memcpy(entry->pdu, hdr, sizeof(iscsi_hdr_t)); + if (data) + memcpy(entry->pdu + sizeof(iscsi_hdr_t), data, data_size); + entry->type = ISCSI_KEVENT_RECV_PDU; + entry->state = PDU_STATE_BUSY; + entry->curr_off = 0; + entry->cp_cnxh = (ulong_t)cp_cnx; + entry->pdu_size = sizeof(iscsi_hdr_t) + data_size; + + spin_lock_bh(&evqueue_lock); + recv_entry_cnt++; + list_add(&entry->item, &evqueue); + spin_unlock_bh(&evqueue_lock); return 0; } +void +iscsi_control_cnx_error(iscsi_cnx_h cp_cnx, int error) +{ +} + static ssize_t write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - if (sp_ctxt.state != SP_STATE_BUSY) + if (xmit.state != PDU_STATE_BUSY) return -EBUSY; - if (sp_ctxt.curr_off + count > sp_ctxt.hdr_size + sp_ctxt.data_size) + if (xmit.curr_off + count > xmit.hdr_size + xmit.data_size) return -EPERM; - if (copy_from_user(&sp_ctxt.spb->pdu[sp_ctxt.curr_off], buf, count)) - count = -EFAULT; - sp_ctxt.curr_off += count; + if (copy_from_user(&xmit.pdu[xmit.curr_off], buf, count)) { + count = -EFAULT; + } else { + xmit.curr_off += count; + } return count; } @@ -148,19 +152,12 @@ write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) static ssize_t read(struct file *file, char *buf, size_t count, loff_t *ppos) { - kevent_t *kevent; - - if (count != sizeof(iscsi_uevent_t)) - return -EIO; - - kevent = iscsi_event_get(1); - if (IS_ERR(kevent)) - return -EAGAIN; - - if (copy_to_user(buf, &kevent->ev, count)) + if (copy_to_user(buf, recv->pdu + + recv->curr_off, count)) { count = -EFAULT; - - kfree(kevent); + } else { + recv->curr_off += count; + } return count; } @@ -180,9 +177,9 @@ close(struct inode *inode, struct file *file) static unsigned int poll(struct file *filp, poll_table *wait) { - poll_wait(filp, &event_wait, wait); + poll_wait(filp, &evwait, wait); - return IS_ERR(iscsi_event_get(0)) ? 0 : POLLIN | POLLRDNORM; + return IS_ERR(recv_entry_get(0)) ? 0 : POLLIN | POLLRDNORM; } static iscsi_kprovider_t* @@ -207,7 +204,7 @@ __create_session(unsigned long ptr) return -EEXIST; handle = (ulong_t)provider->ops.create_session( - (void*)ev.u.c_session.handle, ev.u.c_session.sid, + (void*)ev.u.c_session.session_handle, ev.u.c_session.sid, ev.u.c_session.initial_cmdsn); if (!handle) { return -EIO; @@ -242,7 +239,7 @@ __create_cnx(unsigned long ptr) } handle = (ulong_t)provider->ops.create_cnx( - (void*)ev.u.c_cnx.session_handle, (void*)ev.u.c_cnx.handle, + (void*)ev.u.c_cnx.session_handle, (void*)ev.u.c_cnx.cnx_handle, sock, ev.u.c_cnx.cid); if (!handle) { return -EIO; @@ -290,38 +287,24 @@ __send_pdu_begin(unsigned long ptr) { int rc; iscsi_uevent_t ev; - struct list_head *lh; if ((rc = copy_from_user(&ev, (void *)ptr, sizeof(ev))) < 0) return rc; - if (ev.u.sp_begin.hdr_size + ev.u.sp_begin.data_size > - DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH + sizeof(iscsi_hdr_t) + 4) - return -EPERM; - - if (sp_ctxt.state != SP_STATE_READY) + if (xmit.state != PDU_STATE_READY) return -EBUSY; - sp_ctxt.spb = NULL; - list_for_each(lh, &sp_head) { - sp_item_t *spb; - spb = list_entry(lh, sp_item_t, item); - if (spb && spb->state == SP_STATE_READY) { - spb->state = SP_STATE_BUSY; - sp_ctxt.spb = spb; - break; - } - } - if (sp_ctxt.spb == NULL) { - /* FIXME: allocate up to configured max. */ + xmit.pdu = kmalloc(ev.u.sp_begin.hdr_size + ev.u.sp_begin.data_size, + GFP_KERNEL); + if (xmit.pdu == NULL) { return -ENOMEM; } - sp_ctxt.cnxh = ev.u.sp_begin.cnx_handle; - sp_ctxt.state = SP_STATE_BUSY; - sp_ctxt.hdr_size = ev.u.sp_begin.hdr_size; - sp_ctxt.data_size = ev.u.sp_begin.data_size; - sp_ctxt.curr_off = 0; + xmit.cnxh = ev.u.sp_begin.cnx_handle; + xmit.state = PDU_STATE_BUSY; + xmit.hdr_size = ev.u.sp_begin.hdr_size; + xmit.data_size = ev.u.sp_begin.data_size; + xmit.curr_off = 0; return 0; } @@ -339,19 +322,22 @@ __send_pdu_end(unsigned long ptr) if ((provider = __provider_lookup(ev.provider_id)) == NULL) return -EEXIST; - if (sp_ctxt.state != SP_STATE_BUSY) + if (xmit.state != PDU_STATE_BUSY) return -EPERM; - if (sp_ctxt.cnxh != ev.u.sp_end.cnx_handle) + if (xmit.cnxh != ev.u.sp_end.cnx_handle) return -EPERM; rc = (ulong_t)provider->ops.send_immpdu( - (void*)ev.u.sp_end.cnx_handle, (iscsi_hdr_t*)sp_ctxt.spb->pdu, - sp_ctxt.spb->pdu + sp_ctxt.hdr_size, sp_ctxt.data_size); + (void*)ev.u.sp_end.cnx_handle, (iscsi_hdr_t*)xmit.pdu, + xmit.pdu + xmit.hdr_size, xmit.data_size); if (rc) { return -EIO; } + kfree(xmit.pdu); + xmit.state = PDU_STATE_READY; + if ((rc = copy_to_user(&((iscsi_uevent_t*)ptr)->r.retcode, &rc, sizeof(int))) < 0) { return rc; @@ -361,6 +347,138 @@ __send_pdu_end(unsigned long ptr) } static int +__recv_pdu_begin(unsigned long ptr) +{ + int rc; + iscsi_uevent_t ev; + recv_context_t *entry = NULL; + struct list_head *lh; + + if ((rc = copy_from_user(&ev, (void *)ptr, sizeof(ev))) < 0) + return rc; + + list_for_each(lh, &evqueue_busy) { + entry = list_entry(lh, recv_context_t, item); + if (entry && entry == (void*)ev.u.rp_begin.recv_handle) { + spin_lock_bh(&evqueue_lock); + list_del(&entry->item); + spin_unlock_bh(&evqueue_lock); + break; + } + } + if (entry != (void*)ev.u.rp_begin.recv_handle) + return -EIO; + + ev.r.rp_begin.pdu_handle = (ulong_t)entry->pdu; + ev.r.rp_begin.pdu_size = entry->pdu_size; + + if ((rc = copy_to_user((void*)ptr, &ev, sizeof(ev))) < 0) { + spin_lock_bh(&evqueue_lock); + list_add(&entry->item, &evqueue_busy); + spin_unlock_bh(&evqueue_lock); + return rc; + } + + recv = entry; + + return 0; +} + +static int +__recv_pdu_end(unsigned long ptr) +{ + int rc; + iscsi_uevent_t ev; + + if (recv == NULL) + return -EIO; + + if ((rc = copy_from_user(&ev, (void *)ptr, sizeof(ev))) < 0) + return rc; + + if (ev.u.rp_end.cpcnx_handle != (ulong_t)recv->cp_cnxh) + return -EPERM; + + if (ev.u.rp_end.pdu_handle != (ulong_t)recv->pdu) + return -EPERM; + + kfree(recv->pdu); + kfree(recv); + recv = NULL; + + return 0; +} + +static int +__recv_req(unsigned long ptr) +{ + int rc; + iscsi_uevent_t ev; + recv_context_t *entry; + + if ((rc = copy_from_user(&ev, (void *)ptr, sizeof(ev))) < 0) + return rc; + + entry = recv_entry_get(1); + if (IS_ERR(entry)) + return -EPERM; + + spin_lock_bh(&evqueue_lock); + list_add(&entry->item, &evqueue_busy); + spin_unlock_bh(&evqueue_lock); + + ev.type = entry->type; + ev.r.recv_req.recv_handle = (ulong_t)entry; + ev.r.recv_req.cnx_handle = (ulong_t)entry->cp_cnxh; + + if ((rc = copy_to_user((void*)ptr, &ev, sizeof(ev))) < 0) + return rc; + + return 0; +} + +static int +__set_param(unsigned long ptr) +{ + int rc; + iscsi_uevent_t ev; + iscsi_kprovider_t *provider; + + if ((rc = copy_from_user(&ev, (void *)ptr, sizeof(ev))) < 0) + return rc; + + if ((provider = __provider_lookup(ev.provider_id)) == NULL) + return -EEXIST; + + rc = provider->ops.set_param((void*)ev.u.set_param.cnx_handle, + ev.u.set_param.param, ev.u.set_param.value); + if (rc) + return rc; + + return 0; +} + +static int +__start_cnx(unsigned long ptr) +{ + int rc; + iscsi_uevent_t ev; + iscsi_kprovider_t *provider; + + if ((rc = copy_from_user(&ev, (void *)ptr, sizeof(ev))) < 0) + return rc; + + if ((provider = __provider_lookup(ev.provider_id)) == NULL) + return -EEXIST; + + rc = provider->ops.start_cnx((void*)ev.u.set_param.cnx_handle); + if (rc) + return rc; + + return 0; +} + +static int ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { @@ -376,6 +494,11 @@ ioctl(struct inode *inode, struct file *file, case ISCSI_UEVENT_BIND_CNX: return __bind_cnx(arg); case ISCSI_UEVENT_SEND_PDU_BEGIN: return __send_pdu_begin(arg); case ISCSI_UEVENT_SEND_PDU_END: return __send_pdu_end(arg); + case ISCSI_UEVENT_RECV_PDU_BEGIN: return __recv_pdu_begin(arg); + case ISCSI_UEVENT_RECV_PDU_END: return __recv_pdu_end(arg); + case ISCSI_UEVENT_RECV_REQ: return __recv_req(arg); + case ISCSI_UEVENT_SET_PARAM: return __set_param(arg); + case ISCSI_UEVENT_START_CNX: return __start_cnx(arg); default: return -EPERM; } @@ -399,25 +522,17 @@ static int __init iscsi_init(void) { int rc; - sp_item_t *spb; printk(KERN_INFO "Open-iSCSI Provider Manager, version " ISCSI_VERSION_STR " variant (" ISCSI_DATE_STR ")\n"); - INIT_LIST_HEAD(&sp_head); - - spb = kmalloc(sizeof(sp_item_t), GFP_KERNEL); - if (spb == NULL) { - printk("failed to allocate send pdu buffer\n"); - return -ENOMEM; - } - list_add(&spb->item, &sp_head); - spb->state = SP_STATE_READY; - sp_ctxt.state = SP_STATE_READY; + INIT_LIST_HEAD(&evqueue); + INIT_LIST_HEAD(&evqueue_busy); + evqueue_lock = SPIN_LOCK_UNLOCKED; + xmit.state = PDU_STATE_READY; ctr_major = register_chrdev(0, ctr_name, &ctr_fops); if (ctr_major < 0) { - kfree(spb); printk("failed to register the control device %d\n", ctr_major); return ctr_major; } @@ -427,7 +542,6 @@ iscsi_init(void) rc = iscsi_tcp_register(&provider_table[0].ops, &provider_table[0].caps); if (rc) { - kfree(spb); unregister_chrdev(ctr_major, ctr_name); return rc; } @@ -438,17 +552,8 @@ iscsi_init(void) static void __exit iscsi_exit(void) { - struct list_head *lh, *n; iscsi_tcp_unregister(); unregister_chrdev(ctr_major, ctr_name); - list_for_each_safe(lh, n, &sp_head) { - sp_item_t *spb; - spb = list_entry(lh, sp_item_t, item); - if (spb) { - list_del(&spb->item); - kfree(spb); - } - } } module_init(iscsi_init); diff --git a/usr/ctldev.c b/usr/ctldev.c index 08a5c5a..4ffca5b 100644 --- a/usr/ctldev.c +++ b/usr/ctldev.c @@ -37,29 +37,50 @@ int ctldev_handle(int fd) { - iscsi_uevent_t event; - int res; - - while (1) { - res = read(fd, &event, sizeof(event)); - if (res < 0) { - if (errno == EAGAIN) - return 0; - if (errno == EINTR) - continue; - log_error("got error (%d) when read ctrl_fd", errno); - return 1; + int rc; + iscsi_uevent_t ev; + struct qelem *item; + iscsi_session_t *session = NULL; + iscsi_conn_t *conn = NULL; + + if ((rc = ioctl(fd, ISCSI_UEVENT_RECV_REQ, &ev)) < 0) { + log_error("can't fetch recv event information " + "(%d), retcode %d", errno, rc); + return rc; + } + + if (ev.type == ISCSI_KEVENT_RECV_PDU) { + + /* verify connection */ + item = provider[0].sessions.q_forw; + while (item != &provider[0].sessions) { + int i; + session = (iscsi_session_t *)item; + for (i=0; i<ISCSI_CNX_MAX; i++) { + if (&session->cnx[i] == (iscsi_conn_t*) + ev.r.recv_req.cnx_handle) { + conn = &session->cnx[i]; + break; + } + } + item = item->q_forw; + } + if (conn == NULL) { + log_error("could not verify connection 0x%llx for " + "event RECV_PDU", (uint64_t)(ulong_t)conn); + return -ENXIO; } - log_debug(1, "got event, type %u", event.type); + /* produce an event, so session manager will handle */ + queue_produce(session->queue, EV_CNX_RECV_PDU, conn, + sizeof(ulong_t), (void*)&ev.r.recv_req.recv_handle); + actor_schedule(&session->mainloop); - switch (event.type) { - default: - log_error("%s(%d) %u\n", __FUNCTION__, __LINE__, - event.type); - return -1; - } + } else if (ev.type == ISCSI_KEVENT_CNX_ERROR) { + } else { + log_error("unknown kernel event %d", ev.type); } + return 0; } diff --git a/usr/discovery.c b/usr/discovery.c index 3a793f2..b230f17 100644 --- a/usr/discovery.c +++ b/usr/discovery.c @@ -671,12 +671,10 @@ init_new_session(struct iscsi_sendtargets_config *config) session->cnx[0].ping_timeout = config->cnx_timeo.ping_timeout; session->send_async_text = config->continuous ? config->send_async_text : -1; - session->cnx[0].header_digest = ISCSI_DIGEST_NONE; - session->cnx[0].data_digest = ISCSI_DIGEST_NONE; - session->cnx[0].max_recv_data_segment_len = - DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH; - session->cnx[0].max_xmit_data_segment_len = - DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH; + session->cnx[0].hdrdgst_en = ISCSI_DIGEST_NONE; + session->cnx[0].datadgst_en = ISCSI_DIGEST_NONE; + session->cnx[0].max_recv_dlength = DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH; + session->cnx[0].max_xmit_dlength = DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH; /* OUI and uniqifying number */ session->isid[0] = DRIVER_ISID_0; @@ -24,6 +24,8 @@ #include <fcntl.h> #include <sys/stat.h> #include <sys/file.h> +#define DB_DBM_HSEARCH 1 +#include <db.h> #include "idbm.h" #include "log.h" @@ -21,8 +21,6 @@ #define IDBM_H #include <sys/types.h> -#define DB_DBM_HSEARCH 1 -#include <db.h> #include "initiator.h" #include "config.h" @@ -46,8 +44,8 @@ typedef struct recinfo { } recinfo_t; typedef struct idbm { - DBM *discdb; - DBM *nodedb; + void *discdb; + void *nodedb; char *configfile; node_rec_t nrec; recinfo_t ninfo[MAX_KEYS]; diff --git a/usr/initiator.c b/usr/initiator.c index 8e31dca..48c9e68 100644 --- a/usr/initiator.c +++ b/usr/initiator.c @@ -17,6 +17,7 @@ * See the file COPYING included with this distribution for more details. */ +#include <search.h> #include <string.h> #include <stdlib.h> #include <netdb.h> @@ -232,16 +233,15 @@ session_cnx_create(iscsi_session_t *session, int cid) conn->ping_timeout = cnx->timeo.ping_timeout; /* operational parameters */ - conn->max_recv_data_segment_len = + conn->max_recv_dlength = cnx->iscsi.MaxRecvDataSegmentLength; /* * iSCSI default, unless declared otherwise by the * target during login */ - conn->max_xmit_data_segment_len = - DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH; - conn->header_digest = cnx->iscsi.HeaderDigest; - conn->data_digest = cnx->iscsi.DataDigest; + conn->max_xmit_dlength = DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH; + conn->hdrdgst_en = cnx->iscsi.HeaderDigest; + conn->datadgst_en = cnx->iscsi.DataDigest; /* TCP options */ conn->tcp_window_size = cnx->tcp.window_size; @@ -344,10 +344,10 @@ session_create(node_rec_t *rec) actor_schedule(&session->mainloop); /* session's operational parameters */ - session->initial_r2t = rec->session.iscsi.InitialR2T; - session->immediate_data = rec->session.iscsi.ImmediateData; - session->first_burst_len = rec->session.iscsi.FirstBurstLength; - session->max_burst_len = rec->session.iscsi.MaxBurstLength; + session->initial_r2t_en = rec->session.iscsi.InitialR2T; + session->imm_data_en = rec->session.iscsi.ImmediateData; + session->first_burst = rec->session.iscsi.FirstBurstLength; + session->max_burst = rec->session.iscsi.MaxBurstLength; session->def_time2wait = rec->session.iscsi.DefaultTime2Wait; session->def_time2retain = rec->session.iscsi.DefaultTime2Retain; session->portal_group_tag = rec->tpgt; @@ -368,12 +368,15 @@ session_create(node_rec_t *rec) /* setup authentication variables for the session*/ setup_authentication(session, &rec->session.auth); + insque(&session->item, &provider[0].sessions); + return session; } void session_destroy(iscsi_session_t *session) { + remque(&session->item); queue_flush(session->queue); queue_destroy(session->queue); actor_delete(&session->mainloop); @@ -390,7 +393,7 @@ __ksession_create(iscsi_session_t *session) ev.type = ISCSI_UEVENT_CREATE_SESSION; ev.provider_id = 0; /* FIXME: hardcoded */ - ev.u.c_session.handle = (ulong_t)session; + ev.u.c_session.session_handle = (ulong_t)session; ev.u.c_session.sid = session->id; ev.u.c_session.initial_cmdsn = session->nrec.session.initial_cmdsn; @@ -418,7 +421,7 @@ __ksession_cnx_create(iscsi_session_t *session, iscsi_conn_t *conn) ev.type = ISCSI_UEVENT_CREATE_CNX; ev.provider_id = 0; /* FIXME: hardcoded */ ev.u.c_cnx.session_handle = session->handle; - ev.u.c_cnx.handle = (ulong_t)conn; + ev.u.c_cnx.cnx_handle = (ulong_t)conn; ev.u.c_cnx.socket_fd = conn->socket_fd; ev.u.c_cnx.cid = conn->id; @@ -569,8 +572,280 @@ __ksession_send_pdu_end(iscsi_session_t *session, iscsi_conn_t *conn) #endif static void +__session_ipc_login_cleanup(queue_task_t *qtask, ipc_err_e err) +{ + iscsi_conn_t *conn = qtask->conn; + iscsi_session_t *session = conn->session; + + qtask->u.login.rsp.err = err; + write(qtask->u.login.ipc_fd, &qtask->u.login.rsp, + sizeof(qtask->u.login.rsp)); + close(qtask->u.login.ipc_fd); + free(qtask); + if (conn->login_context.buffer) + free(conn->login_context.buffer); + session_cnx_destroy(session, conn->id); + if (conn->id == 0) + session_destroy(session); +} + +static int +__ksession_set_param(iscsi_conn_t *conn, iscsi_param_e param, uint32_t value) +{ + int rc; + iscsi_uevent_t ev; + + memset(&ev, 0, sizeof(iscsi_uevent_t)); + + ev.type = ISCSI_UEVENT_SET_PARAM; + ev.provider_id = 0; /* FIXME: hardcoded */ + ev.u.set_param.cnx_handle = (ulong_t)conn->handle; + ev.u.set_param.param = param; + ev.u.set_param.value = value; + + if ((rc = ioctl(ctrl_fd, ISCSI_UEVENT_SET_PARAM, &ev)) < 0) { + log_error("can't set operational parameter %d for cnx with " + "id = %d (%d)", param, conn->id, errno); + return rc; + } + + log_debug(3, "set operational parameter %d to %u", + param, value); + + return 0; +} + +static int +__ksession_start_cnx(iscsi_conn_t *conn) +{ + int rc; + iscsi_uevent_t ev; + + memset(&ev, 0, sizeof(iscsi_uevent_t)); + + ev.type = ISCSI_UEVENT_SET_PARAM; + ev.provider_id = 0; /* FIXME: hardcoded */ + ev.u.start_cnx.cnx_handle = (ulong_t)conn->handle; + + if ((rc = ioctl(ctrl_fd, ISCSI_UEVENT_START_CNX, &ev)) < 0) { + log_error("can't start connection 0x%llx with " + "id = %d (%d)", (uint64_t)conn->handle, + conn->id, errno); + return rc; + } + + log_debug(3, "connection 0x%llx operational now", + (uint64_t)conn->handle); + + return 0; +} + +static int +__ksession_recv_pdu_begin(iscsi_conn_t *conn, ulong_t recv_handle, + ulong_t *pdu_handle, int *pdu_size) +{ + int rc; + iscsi_uevent_t ev; + + memset(&ev, 0, sizeof(iscsi_uevent_t)); + + ev.type = ISCSI_UEVENT_RECV_PDU_BEGIN; + ev.provider_id = 0; /* FIXME: hardcoded */ + ev.u.rp_begin.cpcnx_handle = (ulong_t)conn; + ev.u.rp_begin.recv_handle = recv_handle; + + if ((rc = ioctl(ctrl_fd, ISCSI_UEVENT_RECV_PDU_BEGIN, &ev)) < 0) { + log_error("can't initiate recv PDU operation for cnx with " + "id = %d (%d)", conn->id, errno); + return rc; + } + + *pdu_handle = ev.r.rp_begin.pdu_handle; + *pdu_size = ev.r.rp_begin.pdu_size; + + log_debug(3, "recv PDU began, pdu handle 0x%llx size %d", + (uint64_t)*pdu_handle, *pdu_size); + + return 0; +} + +static int +__ksession_recv_pdu_end(iscsi_conn_t *conn, ulong_t pdu_handle) +{ + int rc; + iscsi_uevent_t ev; + + memset(&ev, 0, sizeof(iscsi_uevent_t)); + + ev.type = ISCSI_UEVENT_RECV_PDU_END; + ev.provider_id = 0; /* FIXME: hardcoded */ + ev.u.rp_end.cpcnx_handle = (ulong_t)conn; + ev.u.rp_end.pdu_handle = pdu_handle; + + if ((rc = ioctl(ctrl_fd, ISCSI_UEVENT_RECV_PDU_END, &ev)) < 0) { + log_error("can't finish recv PDU operation for cnx with " + "id = %d (%d)", conn->id, errno); + return rc; + } + + log_debug(3, "recv PDU finished for pdu handle 0x%llx", + (uint64_t)pdu_handle); + + return 0; +} + +static void __session_cnx_recv_pdu(queue_item_t *item) { + ulong_t recv_handle = *(ulong_t*)queue_item_data(item); + iscsi_conn_t *conn = item->context; + iscsi_session_t *session = conn->session; + + if (conn->state == STATE_WAIT_LOGIN_RSP) { + iscsi_login_context_t *c = &conn->login_context; + + conn->recv_handle = recv_handle; + + if (iscsi_login_rsp(session, c)) { + __session_ipc_login_cleanup(c->qtask, + IPC_ERR_LOGIN_FAILURE); + return; + } + + if (conn->current_stage != ISCSI_FULL_FEATURE_PHASE) { + /* more nego. needed! */ + conn->state = STATE_WAIT_LOGIN_RSP; + if (iscsi_login_req(session, c)) { + __session_ipc_login_cleanup(c->qtask, + IPC_ERR_LOGIN_FAILURE); + return; + } + } else { + /* almost! entered full-feature phase */ + + if (login_response_status(conn, c->ret) != + CNX_LOGIN_SUCCESS) { + __session_ipc_login_cleanup(c->qtask, + IPC_ERR_LOGIN_FAILURE); + return; + } + + /* check the login status */ + if (check_iscsi_status_class(session, conn->id, + c->status_class, c->status_detail) != + CNX_LOGIN_SUCCESS) { + __session_ipc_login_cleanup(c->qtask, + IPC_ERR_LOGIN_FAILURE); + return; + } + + /* Entered full-feature phase! */ + + if (__ksession_set_param(conn, + ISCSI_PARAM_MAX_RECV_DLENGTH, + conn->max_recv_dlength)) { + __session_ipc_login_cleanup(c->qtask, + IPC_ERR_LOGIN_FAILURE); + return; + } + if (__ksession_set_param(conn, + ISCSI_PARAM_MAX_XMIT_DLENGTH, + conn->max_xmit_dlength)) { + __session_ipc_login_cleanup(c->qtask, + IPC_ERR_LOGIN_FAILURE); + return; + } + if (__ksession_set_param(conn, + ISCSI_PARAM_HDRDGST_EN, conn->hdrdgst_en)) { + __session_ipc_login_cleanup(c->qtask, + IPC_ERR_LOGIN_FAILURE); + return; + } + if (__ksession_set_param(conn, + ISCSI_PARAM_DATADGST_EN, conn->datadgst_en)) { + __session_ipc_login_cleanup(c->qtask, + IPC_ERR_LOGIN_FAILURE); + return; + } + if (conn->id == 0) { + /* setup session's op. parameters just once */ + if (__ksession_set_param(conn, + ISCSI_PARAM_INITIAL_R2T_EN, + session->initial_r2t_en)) { + __session_ipc_login_cleanup(c->qtask, + IPC_ERR_LOGIN_FAILURE); + return; + } + if (__ksession_set_param(conn, + ISCSI_PARAM_MAX_R2T, + 1 /* FIXME: session->max_r2t */)) { + __session_ipc_login_cleanup(c->qtask, + IPC_ERR_LOGIN_FAILURE); + return; + } + if (__ksession_set_param(conn, + ISCSI_PARAM_IMM_DATA_EN, + session->imm_data_en)) { + __session_ipc_login_cleanup(c->qtask, + IPC_ERR_LOGIN_FAILURE); + return; + } + if (__ksession_set_param(conn, + ISCSI_PARAM_FIRST_BURST, + session->first_burst)) { + __session_ipc_login_cleanup(c->qtask, + IPC_ERR_LOGIN_FAILURE); + return; + } + if (__ksession_set_param(conn, + ISCSI_PARAM_MAX_BURST, + session->max_burst)) { + __session_ipc_login_cleanup(c->qtask, + IPC_ERR_LOGIN_FAILURE); + return; + } + if (__ksession_set_param(conn, + ISCSI_PARAM_PDU_INORDER_EN, + session->pdu_inorder_en)) { + __session_ipc_login_cleanup(c->qtask, + IPC_ERR_LOGIN_FAILURE); + return; + } + if (__ksession_set_param(conn, + ISCSI_PARAM_DATASEQ_INORDER_EN, + session->dataseq_inorder_en)) { + } + if (__ksession_set_param(conn, + ISCSI_PARAM_ERL, + 0 /* FIXME: session->erl */)) { + __session_ipc_login_cleanup(c->qtask, + IPC_ERR_LOGIN_FAILURE); + return; + } + if (__ksession_set_param(conn, + ISCSI_PARAM_IFMARKER_EN, + 0 /* FIXME: session->ifmarker_en */)) { + __session_ipc_login_cleanup(c->qtask, + IPC_ERR_LOGIN_FAILURE); + return; + } + if (__ksession_set_param(conn, + ISCSI_PARAM_OFMARKER_EN, + 0 /* FIXME: session->ofmarker_en */)) { + __session_ipc_login_cleanup(c->qtask, + IPC_ERR_LOGIN_FAILURE); + return; + } + + } + + if (__ksession_start_cnx(conn)) { + __session_ipc_login_cleanup(c->qtask, + IPC_ERR_INTERNAL); + return; + } + } + } } static void @@ -592,68 +867,67 @@ __session_cnx_poll(queue_item_t *item) /* connected! */ + memset(c, 0, sizeof(iscsi_login_context_t)); + actor_delete(&conn->connect_timer); if (conn->id == 0 && __ksession_create(session)) { - qtask->u.login.rsp.err = IPC_ERR_INTERNAL; - goto err; + __session_ipc_login_cleanup(qtask, + IPC_ERR_INTERNAL); + return; } if (__ksession_cnx_create(session, conn)) { - qtask->u.login.rsp.err = IPC_ERR_INTERNAL; - goto err; + __session_ipc_login_cleanup(qtask, + IPC_ERR_INTERNAL); + return; } if (__ksession_cnx_bind(session, conn)) { - qtask->u.login.rsp.err = IPC_ERR_INTERNAL; - goto err; + __session_ipc_login_cleanup(qtask, + IPC_ERR_INTERNAL); + return; } conn->kernel_io = 1; conn->ctrl_fd = ctrl_fd; conn->send_pdu_begin = __ksession_send_pdu_begin; conn->send_pdu_end = __ksession_send_pdu_end; + conn->recv_pdu_begin = __ksession_recv_pdu_begin; + conn->recv_pdu_end = __ksession_recv_pdu_end; + c->qtask = qtask; c->cid = conn->id; c->buffer = calloc(1, DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH); if (!c->buffer) { log_error("failed to allocate recv " "data buffer"); - qtask->u.login.rsp.err = IPC_ERR_NOMEM; - goto err; + __session_ipc_login_cleanup(qtask, + IPC_ERR_NOMEM); + return; } c->bufsize = DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH; if (iscsi_login_begin(session, c)) { - qtask->u.login.rsp.err = IPC_ERR_LOGIN_FAILURE; - goto err; + __session_ipc_login_cleanup(qtask, + IPC_ERR_LOGIN_FAILURE); + return; } + conn->state = STATE_WAIT_LOGIN_RSP; if (iscsi_login_req(session, c)) { - qtask->u.login.rsp.err = IPC_ERR_LOGIN_FAILURE; - goto err; + __session_ipc_login_cleanup(qtask, + IPC_ERR_LOGIN_FAILURE); + return; } - conn->state = STATE_WAIT_PDU_RSP; } else { actor_delete(&conn->connect_timer); /* error during connect */ - qtask->u.login.rsp.err = IPC_ERR_TCP_FAILURE; - goto err; + __session_ipc_login_cleanup(qtask, + IPC_ERR_TCP_FAILURE); } } - - return; - -err: - /* clean connection. write rsp. cleanup session if needed */ - write(qtask->u.login.ipc_fd, &qtask->u.login.rsp, - sizeof(qtask->u.login.rsp)); - close(qtask->u.login.ipc_fd); - free(qtask); - session_cnx_destroy(session, conn->id); - if (conn->id == 0) - session_destroy(session); } static void @@ -661,18 +935,11 @@ __session_cnx_timer(queue_item_t *item) { queue_task_t *qtask = item->context; iscsi_conn_t *conn = qtask->conn; - iscsi_session_t *session = conn->session; if (conn->state == STATE_WAIT_CONNECT) { /* timeout during connect. clean connection. write rsp */ - qtask->u.login.rsp.err = IPC_ERR_TCP_TIMEOUT; - write(qtask->u.login.ipc_fd, &qtask->u.login.rsp, - sizeof(qtask->u.login.rsp)); - close(qtask->u.login.ipc_fd); - free(qtask); - session_cnx_destroy(session, conn->id); - if (conn->id == 0) - session_destroy(session); + __session_ipc_login_cleanup(qtask, IPC_ERR_TCP_TIMEOUT); + return; } } diff --git a/usr/initiator.h b/usr/initiator.h index 39bf562..88e5e22 100644 --- a/usr/initiator.h +++ b/usr/initiator.h @@ -61,7 +61,7 @@ enum iscsi_login_status { typedef enum iscsi_cnx_state_e { STATE_IDLE = 0, STATE_WAIT_CONNECT = 1, - STATE_WAIT_PDU_RSP = 2, + STATE_WAIT_LOGIN_RSP = 2, } iscsi_cnx_state_e; typedef enum iscsi_event_e { @@ -76,6 +76,8 @@ typedef struct iscsi_event { char payload[EVENT_PAYLOAD_MAX]; } iscsi_event_t; +struct queue_task; + typedef struct iscsi_login_context { int cid; char *buffer; @@ -91,6 +93,7 @@ typedef struct iscsi_login_context { int timeout; int final; enum iscsi_login_status ret; + struct queue_task *qtask; } iscsi_login_context_t; struct iscsi_session; @@ -100,11 +103,16 @@ typedef int (*send_pdu_begin_f)(struct iscsi_session *session, struct iscsi_conn *conn, int hdr_size, int data_size); typedef int (*send_pdu_end_f)(struct iscsi_session *session, struct iscsi_conn *conn); +typedef int (*recv_pdu_begin_f)(struct iscsi_conn *conn, ulong_t recv_handle, + ulong_t *pdu_handle, int *pdu_size); +typedef int (*recv_pdu_end_f)(struct iscsi_conn *conn, ulong_t pdu_handle); /* daemon's connection structure */ typedef struct iscsi_conn { + struct qelem item; /* must stay at the top */ int id; ulong_t handle; + ulong_t recv_handle; struct iscsi_session *session; iscsi_login_context_t login_context; uint8_t *rx_buffer; @@ -115,6 +123,8 @@ typedef struct iscsi_conn { int ctrl_fd; send_pdu_begin_f send_pdu_begin; send_pdu_end_f send_pdu_end; + recv_pdu_begin_f recv_pdu_begin; + recv_pdu_end_f recv_pdu_end; /* login state machine */ int current_stage; @@ -142,10 +152,10 @@ typedef struct iscsi_conn { uint32_t exp_statsn; /* negotiated parameters */ - int header_digest; - int data_digest; - int max_recv_data_segment_len; /* the value we declare */ - int max_xmit_data_segment_len; /* the value declared by the target */ + int hdrdgst_en; + int datadgst_en; + int max_recv_dlength; /* the value we declare */ + int max_xmit_dlength; /* the value declared by the target */ } iscsi_conn_t; typedef struct queue_task { @@ -163,13 +173,14 @@ typedef struct queue_task { int ipc_fd; } logout; /* iSCSI requests originated via CTL */ - struct ctlreq_async_ev { - } async_ev; + struct ctlreq_recv_pdu { + } recv_pdu; } u; } queue_task_t; /* daemon's session structure */ typedef struct iscsi_session { + struct qelem item; /* must stay at the top */ int id; ulong_t handle; node_rec_t nrec; /* copy of original Node record in database */ @@ -180,12 +191,12 @@ typedef struct iscsi_session { uint32_t cmdsn; uint32_t exp_cmdsn; uint32_t max_cmdsn; - int immediate_data; - int initial_r2t; - int first_burst_len; - int max_burst_len; - int data_pdu_in_order; - int data_seq_in_order; + int imm_data_en; + int initial_r2t_en; + int first_burst; + int max_burst; + int pdu_inorder_en; + int dataseq_inorder_en; int def_time2wait; int def_time2retain; int type; @@ -434,24 +434,35 @@ iscsi_recv_pdu(iscsi_conn_t *conn, iscsi_hdr_t *hdr, char *end = data + max_data_length; struct sigaction action; struct sigaction old; + ulong_t pdu_handle; + int pdu_size; /* set a timeout, since the socket calls may take a long * time to timeout on their own */ - memset(data, 0, max_data_length); - memset(&action, 0, sizeof (struct sigaction)); - memset(&old, 0, sizeof (struct sigaction)); - action.sa_sigaction = NULL; - action.sa_flags = 0; - action.sa_handler = sigalarm_handler; - sigaction(SIGALRM, &action, &old); - timedout = 0; - alarm(timeout); + if (!conn->kernel_io) { + memset(data, 0, max_data_length); + memset(&action, 0, sizeof (struct sigaction)); + memset(&old, 0, sizeof (struct sigaction)); + action.sa_sigaction = NULL; + action.sa_flags = 0; + action.sa_handler = sigalarm_handler; + sigaction(SIGALRM, &action, &old); + timedout = 0; + alarm(timeout); + } else { + if (conn->recv_pdu_begin(conn, conn->recv_handle, + &pdu_handle, &pdu_size)) { + failed = 1; + goto done; + } + } /* read a response header */ do { rlen = - read(conn->socket_fd, header, sizeof (*hdr) - h_bytes); + read(conn->kernel_io ? conn->ctrl_fd : conn->socket_fd, + header, sizeof (*hdr) - h_bytes); if (timedout) { log_error("socket %d header read timed out", conn->socket_fd); @@ -505,7 +516,8 @@ iscsi_recv_pdu(iscsi_conn_t *conn, iscsi_hdr_t *hdr, d_bytes = 0; while (d_bytes < dlength) { rlen = - read(conn->socket_fd, data + d_bytes, dlength - d_bytes); + read(conn->kernel_io ? conn->ctrl_fd : conn->socket_fd, + data + d_bytes, dlength - d_bytes); if (timedout) { log_error("socket %d data read timed out", conn->socket_fd); @@ -525,9 +537,10 @@ iscsi_recv_pdu(iscsi_conn_t *conn, iscsi_hdr_t *hdr, } } - /* handle PDU data padding */ + /* handle PDU data padding. + * data is padded in case of kernel_io */ pad = dlength % PAD_WORD_LEN; - if (pad) { + if (pad && !conn->kernel_io) { int pad_bytes = pad = PAD_WORD_LEN - pad; char bytes[PAD_WORD_LEN]; @@ -590,13 +603,24 @@ iscsi_recv_pdu(iscsi_conn_t *conn, iscsi_hdr_t *hdr, } } - done: - alarm(0); - sigaction(SIGALRM, &old, NULL); +done: + if (!conn->kernel_io) { + alarm(0); + sigaction(SIGALRM, &old, NULL); + } else { + /* zero Pad area */ + if (pad) + memset(data+dlength, 0, pad); + /* finalyze receive transaction */ + if (conn->recv_pdu_end(conn, pdu_handle)) { + failed = 1; + } + } + if (timedout || failed) { timedout = 0; return 0; - } else { - return h_bytes + ahs_bytes + d_bytes; } + + return h_bytes + ahs_bytes + d_bytes; } diff --git a/usr/login.c b/usr/login.c index 6c17466..6cf814f 100644 --- a/usr/login.c +++ b/usr/login.c @@ -337,9 +337,9 @@ get_op_params_text_keys(iscsi_session_t *session, int cid, &value_end)) { if (session->type == ISCSI_SESSION_TYPE_NORMAL) { if (value && (strcmp(value, "Yes") == 0)) - session->initial_r2t = 1; + session->initial_r2t_en = 1; else - session->initial_r2t = 0; + session->initial_r2t_en = 0; } else session->irrelevant_keys_bitmap |= IRRELEVANT_INITIALR2T; @@ -348,21 +348,21 @@ get_op_params_text_keys(iscsi_session_t *session, int cid, &value_end)) { if (session->type == ISCSI_SESSION_TYPE_NORMAL) { if (value && (strcmp(value, "Yes") == 0)) - session->immediate_data = 1; + session->imm_data_en = 1; else - session->immediate_data = 0; + session->imm_data_en = 0; } else session->irrelevant_keys_bitmap |= IRRELEVANT_IMMEDIATEDATA; text = value_end; } else if (iscsi_find_key_value("MaxRecvDataSegmentLength", text, end, &value, &value_end)) { - conn->max_xmit_data_segment_len = strtoul(value, NULL, 0); + conn->max_xmit_dlength = strtoul(value, NULL, 0); text = value_end; } else if (iscsi_find_key_value("FirstBurstLength", text, end, &value, &value_end)) { if (session->type == ISCSI_SESSION_TYPE_NORMAL) - session->first_burst_len = strtoul(value, NULL, 0); + session->first_burst = strtoul(value, NULL, 0); else session->irrelevant_keys_bitmap |= IRRELEVANT_FIRSTBURSTLENGTH; @@ -374,7 +374,7 @@ get_op_params_text_keys(iscsi_session_t *session, int cid, * R2Ts, but record it anwyay */ if (session->type == ISCSI_SESSION_TYPE_NORMAL) - session->max_burst_len = strtoul(value, NULL, 0); + session->max_burst = strtoul(value, NULL, 0); else session->irrelevant_keys_bitmap |= IRRELEVANT_MAXBURSTLENGTH; @@ -382,8 +382,8 @@ get_op_params_text_keys(iscsi_session_t *session, int cid, } else if (iscsi_find_key_value("HeaderDigest", text, end, &value, &value_end)) { if (strcmp(value, "None") == 0) { - if (conn->header_digest != ISCSI_DIGEST_CRC32C) - conn->header_digest = ISCSI_DIGEST_NONE; + if (conn->hdrdgst_en != ISCSI_DIGEST_CRC32C) + conn->hdrdgst_en = ISCSI_DIGEST_NONE; else { log_error("Login negotiation " "failed, HeaderDigest=CRC32C " @@ -392,8 +392,8 @@ get_op_params_text_keys(iscsi_session_t *session, int cid, return LOGIN_NEGOTIATION_FAILED; } } else if (strcmp(value, "CRC32C") == 0) { - if (conn->header_digest != ISCSI_DIGEST_NONE) - conn->header_digest = ISCSI_DIGEST_CRC32C; + if (conn->hdrdgst_en != ISCSI_DIGEST_NONE) + conn->hdrdgst_en = ISCSI_DIGEST_CRC32C; else { log_error("Login negotiation " "failed, HeaderDigest=None is " @@ -409,8 +409,8 @@ get_op_params_text_keys(iscsi_session_t *session, int cid, } else if (iscsi_find_key_value("DataDigest", text, end, &value, &value_end)) { if (strcmp(value, "None") == 0) { - if (conn->data_digest != ISCSI_DIGEST_CRC32C) - conn->data_digest = ISCSI_DIGEST_NONE; + if (conn->datadgst_en != ISCSI_DIGEST_CRC32C) + conn->datadgst_en = ISCSI_DIGEST_NONE; else { log_error("Login negotiation " "failed, DataDigest=CRC32C " @@ -418,8 +418,8 @@ get_op_params_text_keys(iscsi_session_t *session, int cid, return LOGIN_NEGOTIATION_FAILED; } } else if (strcmp(value, "CRC32C") == 0) { - if (conn->data_digest != ISCSI_DIGEST_NONE) - conn->data_digest = ISCSI_DIGEST_CRC32C; + if (conn->datadgst_en != ISCSI_DIGEST_NONE) + conn->datadgst_en = ISCSI_DIGEST_CRC32C; else { log_error("Login negotiation " "failed, DataDigest=None is " @@ -460,9 +460,9 @@ get_op_params_text_keys(iscsi_session_t *session, int cid, &value_end)) { if (session->type == ISCSI_SESSION_TYPE_NORMAL) { if (value && strcmp(value, "Yes") == 0) - session->data_pdu_in_order = 1; + session->pdu_inorder_en = 1; else - session->data_pdu_in_order = 0; + session->pdu_inorder_en = 0; } else session->irrelevant_keys_bitmap |= IRRELEVANT_DATAPDUINORDER; @@ -471,9 +471,9 @@ get_op_params_text_keys(iscsi_session_t *session, int cid, &value, &value_end)) { if (session->type == ISCSI_SESSION_TYPE_NORMAL) if (value && strcmp(value, "Yes") == 0) - session->data_seq_in_order = 1; + session->dataseq_inorder_en = 1; else - session->data_seq_in_order = 0; + session->dataseq_inorder_en = 0; else session->irrelevant_keys_bitmap |= IRRELEVANT_DATASEQUENCEINORDER; @@ -724,20 +724,20 @@ add_params_normal_session(iscsi_session_t *session, iscsi_hdr_t *pdu, /* these are only relevant for normal sessions */ if (!iscsi_add_text(pdu, data, max_data_length, "InitialR2T", - session->initial_r2t ? "Yes" : "No")) + session->initial_r2t_en ? "Yes" : "No")) return 0; if (!iscsi_add_text(pdu, data, max_data_length, "ImmediateData", - session->immediate_data ? "Yes" : "No")) + session->imm_data_en ? "Yes" : "No")) return 0; - sprintf(value, "%d", session->max_burst_len); + sprintf(value, "%d", session->max_burst); if (!iscsi_add_text(pdu, data, max_data_length, "MaxBurstLength", value)) return 0; - sprintf(value, "%d",session->first_burst_len); + sprintf(value, "%d",session->first_burst); if (!iscsi_add_text(pdu, data, max_data_length, "FirstBurstLength", value)) return 0; @@ -864,7 +864,7 @@ static int fill_crc_digest_text(iscsi_conn_t *conn, iscsi_hdr_t *pdu, char *data, int max_data_length) { - switch (conn->header_digest) { + switch (conn->hdrdgst_en) { case ISCSI_DIGEST_NONE: if (!iscsi_add_text(pdu, data, max_data_length, "HeaderDigest", "None")) @@ -888,7 +888,7 @@ fill_crc_digest_text(iscsi_conn_t *conn, iscsi_hdr_t *pdu, break; } - switch (conn->data_digest) { + switch (conn->datadgst_en) { case ISCSI_DIGEST_NONE: if (!iscsi_add_text(pdu, data, max_data_length, "DataDigest", "None")) @@ -939,7 +939,7 @@ fill_op_params_text(iscsi_session_t *session, int cid, iscsi_hdr_t *pdu, if (!fill_crc_digest_text(conn, pdu, data, max_data_length)) return 0; - sprintf(value, "%d", conn->max_recv_data_segment_len); + sprintf(value, "%d", conn->max_recv_dlength); if (!iscsi_add_text(pdu, data, max_data_length, "MaxRecvDataSegmentLength", value)) return 0; @@ -1039,9 +1039,9 @@ fill_security_params_text(iscsi_session_t *session, int cid, iscsi_hdr_t *pdu, * keys, or want to offer vendor-specific keys */ if (session->type == ISCSI_SESSION_TYPE_DISCOVERY) - if ((conn->header_digest != ISCSI_DIGEST_NONE) || - (conn->data_digest != ISCSI_DIGEST_NONE) || - (conn->max_recv_data_segment_len != + if ((conn->hdrdgst_en != ISCSI_DIGEST_NONE) || + (conn->datadgst_en != ISCSI_DIGEST_NONE) || + (conn->max_recv_dlength != DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH) || session->vendor_specific_keys) conn->next_stage = @@ -1365,7 +1365,7 @@ iscsi_login_req(iscsi_session_t *session, iscsi_login_context_t *c) goto done; } - conn->state = STATE_WAIT_PDU_RSP; + conn->state = STATE_WAIT_LOGIN_RSP; return 0; done: |