From 9dca10af955255f9806942cb3370d4a86660a655 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Sat, 29 Jan 2011 22:50:43 -0600 Subject: Use pass through interface for sendtargets (take4) Currenly offload cards like bnx2i, be2iscsi, cxgb3i must use a normal eth for discovery. This patch allows us to do discovery using the iscsi class passthrough interface. Note1 that the dirver must set the CAP_TEXT_NEGO setting, which might requires scsi-misc. Limitations of patch: - MaxRecvDataSegmentLength is limited to 8K for discovery sessions when offload is used. V3: - bug fixes from Eddie Wai to call start conn after we have logged in. Fixed set param not setting all settings. Misc cleanups. V4: - fix iscsistart segfault due to missing initialization. --- include/iscsi_if.h | 6 +- usr/Makefile | 15 +- usr/config.h | 3 +- usr/discovery.c | 772 +++++++++++++++++++++++++++---------------------- usr/idbm.c | 51 ++-- usr/initiator.c | 759 ++++++++---------------------------------------- usr/initiator.h | 40 ++- usr/initiator_common.c | 600 ++++++++++++++++++++++++++++++++++++++ usr/io.c | 47 +-- usr/iscsi_ipc.h | 20 ++ usr/iscsi_sysfs.c | 7 +- usr/iscsi_timer.c | 86 ++++++ usr/iscsi_timer.h | 28 ++ usr/iscsiadm.c | 2 +- usr/iscsid.c | 11 +- usr/iscsid.h | 1 - usr/iscsistart.c | 4 +- usr/login.c | 80 ++++- usr/mgmt_ipc.c | 12 - usr/mgmt_ipc.h | 9 +- usr/netlink.c | 120 +++++--- usr/transport.c | 3 +- 22 files changed, 1554 insertions(+), 1122 deletions(-) create mode 100644 usr/initiator_common.c create mode 100644 usr/iscsi_timer.c create mode 100644 usr/iscsi_timer.h diff --git a/include/iscsi_if.h b/include/iscsi_if.h index be72e1a..50a09cb 100644 --- a/include/iscsi_if.h +++ b/include/iscsi_if.h @@ -65,6 +65,8 @@ enum iscsi_uevent_e { ISCSI_UEVENT_PATH_UPDATE = UEVENT_BASE + 20, + ISCSI_UEVENT_MAX = ISCSI_UEVENT_PATH_UPDATE, + /* up events */ ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1, ISCSI_KEVENT_CONN_ERROR = KEVENT_BASE + 2, @@ -75,6 +77,8 @@ enum iscsi_uevent_e { ISCSI_KEVENT_PATH_REQ = KEVENT_BASE + 7, ISCSI_KEVENT_IF_DOWN = KEVENT_BASE + 8, + + ISCSI_KEVENT_MAX = ISCSI_KEVENT_IF_DOWN, }; enum iscsi_tgt_dscvr { @@ -386,7 +390,7 @@ enum iscsi_host_param { #define CAP_HDRDGST 0x10 #define CAP_DATADGST 0x20 #define CAP_MULTI_CONN 0x40 -#define CAP_TEXT_NEGO 0x80 +#define CAP_TEXT_NEGO 0x80 /* support for text requests */ #define CAP_MARKERS 0x100 #define CAP_FW_DB 0x200 #define CAP_SENDTARGETS_OFFLOAD 0x400 /* offload discovery process */ diff --git a/usr/Makefile b/usr/Makefile index b02a706..7d0d9fe 100644 --- a/usr/Makefile +++ b/usr/Makefile @@ -37,12 +37,13 @@ PROGRAMS = iscsid iscsiadm iscsistart # libc compat files SYSDEPS_SRCS = $(wildcard ../utils/sysdeps/*.o) # sources shared between iscsid, iscsiadm and iscsistart -ISCSI_LIB_SRCS = iscsi_util.o io.o auth.o login.o log.o md5.o sha1.o iface.o \ - idbm.o sysfs.o host.o session_info.o iscsi_sysfs.o iscsi_net_util.o \ - iscsid_req.o $(SYSDEPS_SRCS) +ISCSI_LIB_SRCS = iscsi_util.o io.o auth.o iscsi_timer.o login.o log.o md5.o \ + sha1.o iface.o idbm.o sysfs.o host.o session_info.o iscsi_sysfs.o \ + iscsi_net_util.o iscsid_req.o transport.o cxgbi.o be2iscsi.o \ + initiator_common.o $(IPC_OBJ) $(SYSDEPS_SRCS) # core initiator files -INITIATOR_SRCS = initiator.o scsi.o actor.o event_poll.o mgmt_ipc.o \ - transport.o cxgbi.o be2iscsi.o +INITIATOR_SRCS = initiator.o scsi.o actor.o event_poll.o mgmt_ipc.o + # fw boot files FW_BOOT_SRCS = $(wildcard ../utils/fwparam_ibft/*.o) @@ -51,14 +52,14 @@ DISCOVERY_SRCS = $(FW_BOOT_SRCS) strings.o discovery.o all: $(PROGRAMS) -iscsid: $(ISCSI_LIB_SRCS) $(IPC_OBJ) $(INITIATOR_SRCS) $(DISCOVERY_SRCS) \ +iscsid: $(ISCSI_LIB_SRCS) $(INITIATOR_SRCS) $(DISCOVERY_SRCS) \ iscsid.o session_mgmt.o discoveryd.o $(CC) $(CFLAGS) $^ -o $@ -L../utils/open-isns -lisns -lcrypto iscsiadm: $(ISCSI_LIB_SRCS) $(DISCOVERY_SRCS) iscsiadm.o session_mgmt.o $(CC) $(CFLAGS) $^ -o $@ -L../utils/open-isns -lisns -lcrypto -iscsistart: $(IPC_OBJ) $(ISCSI_LIB_SRCS) $(INITIATOR_SRCS) $(FW_BOOT_SRCS) \ +iscsistart: $(ISCSI_LIB_SRCS) $(INITIATOR_SRCS) $(FW_BOOT_SRCS) \ iscsistart.o statics.o $(CC) $(CFLAGS) -static $^ -o $@ clean: diff --git a/usr/config.h b/usr/config.h index b553481..5cb4d56 100644 --- a/usr/config.h +++ b/usr/config.h @@ -141,7 +141,8 @@ struct iscsi_sendtargets_config { int discoveryd_poll_inval; struct iscsi_auth_config auth; struct iscsi_connection_timeout_config conn_timeo; - struct iscsi_conn_operational_config iscsi; + struct iscsi_conn_operational_config conn_conf; + struct iscsi_session_operational_config session_conf; }; struct iscsi_isns_config { diff --git a/usr/discovery.c b/usr/discovery.c index e34cc63..566ef6e 100644 --- a/usr/discovery.c +++ b/usr/discovery.c @@ -43,6 +43,11 @@ #include "fw_context.h" #include "iscsid_req.h" #include "iscsi_util.h" +#include "transport.h" +#include "iscsi_sysfs.h" +#include "iscsi_ipc.h" +#include "iface.h" +#include "iscsi_timer.h" /* libisns includes */ #include "isns.h" #include "paths.h" @@ -54,10 +59,9 @@ #define DISCOVERY_NEED_RECONNECT 0xdead0001 -static int rediscover = 0; - static char initiator_name[TARGET_NAME_MAXLEN + 1]; static char initiator_alias[TARGET_NAME_MAXLEN + 1]; +static struct iscsi_ev_context ipc_ev_context; static int request_initiator_name(void) { @@ -537,19 +541,13 @@ static int add_portal(struct list_head *rec_list, discovery_rec_t *drec, char *targetname, char *address, char *port, char *tag) { struct sockaddr_storage ss; - char host[NI_MAXHOST]; struct node_rec *rec; - /* resolve the address, in case it was a DNS name */ if (resolve_address(address, port, &ss)) { log_error("cannot resolve %s", address); return 0; } - /* convert the resolved name to text */ - getnameinfo((struct sockaddr *) &ss, sizeof(ss), - host, sizeof(host), NULL, 0, NI_NUMERICHOST); - rec = calloc(1, sizeof(*rec)); if (!rec) return 0; @@ -576,7 +574,7 @@ static int add_portal(struct list_head *rec_list, discovery_rec_t *drec, static int add_target_record(char *name, char *end, discovery_rec_t *drec, - struct list_head *rec_list, char *default_port) + struct list_head *rec_list) { char *text = NULL; char *nul = name; @@ -619,11 +617,16 @@ add_target_record(char *name, char *end, discovery_rec_t *drec, log_error("no default address known for target %s", name); return 0; - } else if (!add_portal(rec_list, drec, name, drec->address, - default_port, NULL)) { - log_error("failed to add default portal, ignoring " - "target %s", name); - return 0; + } else { + char default_port[NI_MAXSERV]; + + sprintf(default_port, "%d", drec->port); + if (!add_portal(rec_list, drec, name, drec->address, + default_port, NULL)) { + log_error("failed to add default portal, " + "ignoring target %s", name); + return 0; + } } /* finished adding the default */ return 1; @@ -675,8 +678,7 @@ add_target_record(char *name, char *end, discovery_rec_t *drec, static int process_sendtargets_response(struct str_buffer *sendtargets, int final, discovery_rec_t *drec, - struct list_head *rec_list, - char *default_port) + struct list_head *rec_list) { char *start = str_buffer_data(sendtargets); char *text = start; @@ -727,8 +729,7 @@ process_sendtargets_response(struct str_buffer *sendtargets, * "TargetName=" prefix. */ if (!add_target_record(record + 11, text, - drec, rec_list, - default_port)) { + drec, rec_list)) { log_error( "failed to add target record"); str_truncate_buffer(sendtargets, 0); @@ -756,7 +757,7 @@ process_sendtargets_response(struct str_buffer *sendtargets, "line %s", record, record); if (add_target_record (record + 11, text, - drec, rec_list, default_port)) { + drec, rec_list)) { num_targets++; record = NULL; str_truncate_buffer(sendtargets, 0); @@ -786,110 +787,42 @@ process_sendtargets_response(struct str_buffer *sendtargets, return 1; } -static void -clear_timer(struct timeval *timer) -{ - memset(timer, 0, sizeof (*timer)); -} - -/* set timer to now + seconds */ -static void -set_timer(struct timeval *timer, int seconds) -{ - if (timer) { - memset(timer, 0, sizeof (*timer)); - gettimeofday(timer, NULL); - - timer->tv_sec += seconds; - } -} - -static int -timer_expired(struct timeval *timer) -{ - struct timeval now; - - /* no timer, can't have expired */ - if ((timer == NULL) || ((timer->tv_sec == 0) && (timer->tv_usec == 0))) - return 0; - - memset(&now, 0, sizeof (now)); - gettimeofday(&now, NULL); - - if (now.tv_sec > timer->tv_sec) - return 1; - if ((now.tv_sec == timer->tv_sec) && (now.tv_usec >= timer->tv_usec)) - return 1; - return 0; -} - -static int -msecs_until(struct timeval *timer) +static void iscsi_free_session(struct iscsi_session *session) { - struct timeval now; - int msecs; - long partial; - - /* no timer, can't have expired, infinite time til it expires */ - if ((timer == NULL) || ((timer->tv_sec == 0) && (timer->tv_usec == 0))) - return -1; - - memset(&now, 0, sizeof (now)); - gettimeofday(&now, NULL); - - /* already expired? */ - if (now.tv_sec > timer->tv_sec) - return 0; - if ((now.tv_sec == timer->tv_sec) && (now.tv_usec >= timer->tv_usec)) - return 0; - - /* not expired yet, do the math */ - partial = timer->tv_usec - now.tv_usec; - if (partial < 0) { - partial += 1000 * 1000; - msecs = (partial + 500) / 1000; - msecs += (timer->tv_sec - now.tv_sec - 1) * 1000; - } else { - msecs = (partial + 500) / 1000; - msecs += (timer->tv_sec - now.tv_sec) * 1000; - } - - return msecs; + list_del_init(&session->list); + free(session); } static iscsi_session_t * -init_new_session(struct iscsi_sendtargets_config *config, - struct iface_rec *iface) +iscsi_alloc_session(struct iscsi_sendtargets_config *config, + struct iface_rec *iface) { iscsi_session_t *session; session = calloc(1, sizeof (*session)); if (session == NULL) - goto done; + return NULL; + + session->t = iscsi_sysfs_get_transport_by_name(iface->transport_name); + if (!session->t) { + log_error("iSCSI driver %s is not loaded. Load the module " + "then retry the command.\n", iface->transport_name); + goto fail; + } + INIT_LIST_HEAD(&session->list); /* initialize the session's leading connection */ + session->conn[0].id = 0; session->conn[0].socket_fd = -1; + session->conn[0].session = session; session->conn[0].login_timeout = config->conn_timeo.login_timeout; session->conn[0].auth_timeout = config->conn_timeo.auth_timeout; session->conn[0].active_timeout = config->conn_timeo.active_timeout; - session->conn[0].hdrdgst_en = ISCSI_DIGEST_NONE; - session->conn[0].datadgst_en = ISCSI_DIGEST_NONE; - - session->conn[0].max_recv_dlength = - config->iscsi.MaxRecvDataSegmentLength; - if (session->conn[0].max_recv_dlength < ISCSI_MIN_MAX_RECV_SEG_LEN || - session->conn[0].max_recv_dlength > ISCSI_MAX_MAX_RECV_SEG_LEN) { - log_error("Invalid iscsi.MaxRecvDataSegmentLength. Must be " - "within %u and %u. Setting to %u.", - ISCSI_MIN_MAX_RECV_SEG_LEN, - ISCSI_MAX_MAX_RECV_SEG_LEN, - DEF_INI_DISC_MAX_RECV_SEG_LEN); - session->conn[0].max_recv_dlength = - DEF_INI_DISC_MAX_RECV_SEG_LEN; - } - session->conn[0].max_xmit_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN; - + session->conn[0].noop_out_timeout = 0; + session->conn[0].noop_out_interval = 0; session->reopen_cnt = config->reopen_max + 1; + iscsi_copy_operational_params(&session->conn[0], &config->session_conf, + &config->conn_conf); /* OUI and uniqifying number */ session->isid[0] = DRIVER_ISID_0; @@ -908,102 +841,27 @@ init_new_session(struct iscsi_sendtargets_config *config, if (initiator_name[0] == '\0') { log_error("Cannot perform discovery. Initiatorname " "required."); - free(session); - return NULL; + goto fail; } } + iface_copy(&session->nrec.iface, iface); session->initiator_name = initiator_name; session->initiator_alias = initiator_alias; session->portal_group_tag = PORTAL_GROUP_TAG_UNKNOWN; session->type = ISCSI_SESSION_TYPE_DISCOVERY; -done: - return session; -} - + session->id = -1; -static int -setup_authentication(iscsi_session_t *session, - discovery_rec_t *drec, - struct iscsi_sendtargets_config *config) -{ - int rc; - - rc = 1; - - /* if we have any incoming credentials, we insist on authenticating - * the target or not logging in at all - */ - if (config->auth.username_in[0] - || config->auth.password_in_length) { - session->bidirectional_auth = 1; + /* setup authentication variables for the session*/ + if (iscsi_setup_authentication(session, &config->auth)) + goto fail; - /* sanity check the config */ - if (config->auth.password_length == 0) { - log_error( - "discovery process to %s:%d has incoming " - "authentication credentials but has no outgoing " - "credentials configured", - drec->address, drec->port); - log_error( - "discovery process to %s:%d exiting, bad " - "configuration", - drec->address, drec->port); - rc = 0; - goto done; - } - } else { - /* no or 1-way authentication */ - session->bidirectional_auth = 0; - } + list_add_tail(&session->list, &session->t->sessions); + return session; - /* copy in whatever credentials we have */ - strlcpy(session->username, config->auth.username, - sizeof (session->username)); - session->username[sizeof (session->username) - 1] = '\0'; - if ((session->password_length = config->auth.password_length)) - memcpy(session->password, config->auth.password, - session->password_length); - - strlcpy(session->username_in, config->auth.username_in, - sizeof (session->username_in)); - session->username_in[sizeof (session->username_in) - 1] = '\0'; - if ((session->password_in_length = - config->auth.password_in_length)) - memcpy(session->password_in, config->auth.password_in, - session->password_in_length); - - if (session->password_length || session->password_in_length) { - /* setup the auth buffers */ - session->auth_buffers[0].address = &session->auth_client_block; - session->auth_buffers[0].length = - sizeof (session->auth_client_block); - session->auth_buffers[1].address = - &session->auth_recv_string_block; - session->auth_buffers[1].length = - sizeof (session->auth_recv_string_block); - - session->auth_buffers[2].address = - &session->auth_send_string_block; - session->auth_buffers[2].length = - sizeof (session->auth_send_string_block); - - session->auth_buffers[3].address = - &session->auth_recv_binary_block; - session->auth_buffers[3].length = - sizeof (session->auth_recv_binary_block); - - session->auth_buffers[4].address = - &session->auth_send_binary_block; - session->auth_buffers[4].length = - sizeof (session->auth_send_binary_block); - - session->num_auth_buffers = 5; - } else { - session->num_auth_buffers = 0; - } - done: - return(rc); +fail: + free(session); + return NULL; } static int @@ -1012,7 +870,6 @@ process_recvd_pdu(struct iscsi_hdr *pdu, struct list_head *rec_list, iscsi_session_t *session, struct str_buffer *sendtargets, - char *default_port, int *active, int *valid_text, char *data) @@ -1057,8 +914,7 @@ process_recvd_pdu(struct iscsi_hdr *pdu, process_sendtargets_response(sendtargets, final, drec, - rec_list, - default_port); + rec_list); if (final) { /* SendTargets exchange is now complete @@ -1090,11 +946,9 @@ process_recvd_pdu(struct iscsi_hdr *pdu, } /* - * Make a best effort to logout the session, then disconnect the - * socket. + * Make a best effort to logout the session. */ -static void -iscsi_logout_and_disconnect(iscsi_session_t * session) +static void iscsi_logout(iscsi_session_t * session) { struct iscsi_logout logout_req; struct iscsi_logout_rsp logout_resp; @@ -1122,7 +976,7 @@ iscsi_logout_and_disconnect(iscsi_session_t * session) if (!rc) { log_error( "iscsid: iscsi_logout - failed to send logout PDU."); - goto done; + return; } /* @@ -1132,117 +986,273 @@ iscsi_logout_and_disconnect(iscsi_session_t * session) rc = iscsi_io_recv_pdu(&session->conn[0], (struct iscsi_hdr *)&logout_resp, ISCSI_DIGEST_NONE, NULL, 0, ISCSI_DIGEST_NONE, 1); - if (!rc) { + if (rc < 0) { log_error("iscsid: logout - failed to receive logout resp"); - goto done; + return; } if (logout_resp.response != ISCSI_LOGOUT_SUCCESS) { log_error("iscsid: logout failed - response = 0x%x", logout_resp.response); } +} + +static void iscsi_destroy_session(struct iscsi_session *session) +{ + struct iscsi_transport *t = session->t; + struct iscsi_conn *conn = &session->conn[0]; + int rc; + + if (session->id == -1) + return; + + if (!(t->caps & CAP_TEXT_NEGO)) { + iscsi_io_disconnect(&session->conn[0]); + goto done; + } + + log_debug(2, "%s ep disconnect", __FUNCTION__); + t->template->ep_disconnect(conn); + + log_debug(2, "stop conn"); + rc = ipc->stop_conn(session->t->handle, session->id, + conn->id, STOP_CONN_TERM); + if (rc) { + log_error("Could not stop conn %d:%d cleanly (err %d)\n", + session->id, conn->id, rc); + goto done; + } + log_debug(2, "%s destroy conn", __FUNCTION__); + rc = ipc->destroy_conn(session->t->handle, session->id, conn->id); + if (rc) { + log_error("Could not safely destroy conn %d:%d (err %d)", + session->id, conn->id, rc); + goto done; + } + + log_debug(2, "%s destroy session", __FUNCTION__); + rc = ipc->destroy_session(session->t->handle, session->id); + if (rc) + log_error("Could not safely destroy session %d (err %d)", + session->id, rc); done: - /* - * Close the socket. - */ - iscsi_io_disconnect(&session->conn[0]); + if (conn->socket_fd >= 0) { + ipc->ctldev_close(); + conn->socket_fd = -1; + } + session->id = -1; } -int discovery_sendtargets(void *fndata, struct iface_rec *iface, - struct list_head *rec_list) +static int iscsi_create_leading_conn(struct iscsi_session *session) { - discovery_rec_t *drec = fndata; - iscsi_session_t *session; - struct pollfd pfd; - struct iscsi_hdr pdu_buffer; - struct iscsi_hdr *pdu = &pdu_buffer; - char *data = NULL; - int active = 0, valid_text = 0; - struct timeval connection_timer; - int timeout; - int rc; - struct str_buffer sendtargets; - uint8_t status_class = 0, status_detail = 0; - unsigned int login_failures = 0, data_len; - int login_delay = 0; - struct sockaddr_storage ss; - char host[NI_MAXHOST], serv[NI_MAXSERV], default_port[NI_MAXSERV]; - struct iscsi_sendtargets_config *config = &drec->u.sendtargets; + struct iface_rec *iface = &session->nrec.iface; + struct iscsi_transport *t = session->t; + struct iscsi_conn *conn = &session->conn[0]; + uint32_t host_no; + int rc, sleep_count = 0; + + if (!(t->caps & CAP_TEXT_NEGO)) { + /* + * If the LLD does not support TEXT PDUs then we do + * discovery in userspace. + */ + session->use_ipc = 0; - /* initial setup */ - log_debug(1, "starting sendtargets discovery, address %s:%d, ", - drec->address, drec->port); - memset(&pdu_buffer, 0, sizeof (pdu_buffer)); - clear_timer(&connection_timer); + if (!iscsi_io_connect(conn)) + return ENOTCONN; - /* allocate a new session, and initialize default values */ - session = init_new_session(config, iface); - if (session == NULL) { - log_error("Discovery process to %s:%d failed to " - "create a discovery session.", - drec->address, drec->port); - return 1; + session->id = 1; + return 0; } + session->use_ipc = 1; - log_debug(4, "sendtargets discovery to %s:%d using " - "isid 0x%02x%02x%02x%02x%02x%02x", - drec->address, drec->port, session->isid[0], - session->isid[1], session->isid[2], session->isid[3], - session->isid[4], session->isid[5]); + /* + * for software this is the tcp socket fd set in iscsi_io_connect + * and for offload this is the iscsi netlink socket fd + */ + conn->socket_fd = ipc->ctldev_open(); + if (conn->socket_fd < 0) { + log_error("Could not open netlink interface (err %d)\n", + errno); + return errno; + } - /* allocate data buffers for SendTargets data */ - data = malloc(session->conn[0].max_recv_dlength); - if (!data) { - rc = 1; - goto free_session; + host_no = iscsi_sysfs_get_host_no_from_hwinfo(iface, &rc); + if (!rc) { + /* + * if the netdev or mac was set, then we are going to want + * to want to bind the all the conns/eps to a specific host + * if offload is used. + */ + session->conn[0].bind_ep = 1; + session->hostno = host_no; } - data_len = session->conn[0].max_recv_dlength; - str_init_buffer(&sendtargets, 0); + rc = iscsi_host_set_net_params(iface, session); + if (rc) { + log_error("Could not set host net params (err %d)\n", + rc); + goto close_ipc; + } - sprintf(default_port, "%d", drec->port); - /* resolve the DiscoveryAddress to an IP address */ - if (resolve_address(drec->address, default_port, &ss)) { - log_error("cannot resolve host name %s", drec->address); - rc = 1; - goto free_sendtargets; + /* create interconnect endpoint */ + log_debug(2, "%s discovery ep connect\n", __FUNCTION__); + rc = t->template->ep_connect(conn, 1); + if (rc < 0) { + rc = ENOTCONN; + goto fail; } - log_debug(4, "discovery timeouts: login %d, reopen_cnt %d, auth %d.", - session->conn[0].login_timeout, session->reopen_cnt, - session->conn[0].auth_timeout); + do { + rc = t->template->ep_poll(conn, 1); + if (rc < 0) + goto disconnect; + else if (rc == 0) { + if (sleep_count == conn->login_timeout) { + rc = ETIMEDOUT; + goto disconnect; + } + sleep_count++; + sleep(1); + } else + break; + } while (1); - /* setup authentication variables for the session*/ - rc = setup_authentication(session, drec, config); - if (rc == 0) { - rc = 1; - goto free_sendtargets; + log_debug(2, "%s discovery create session\n", __FUNCTION__); + /* create kernel structs */ + rc = ipc->create_session(session->t->handle, + conn->transport_ep_handle, 1, 32, 1, + &session->id, &host_no); + if (rc) { + log_error("Could not create kernel session (err %d).\n", rc); + goto disconnect; + } + log_debug(2, "%s discovery created session %u\n", __FUNCTION__, + session->id); + session->isid[3] = session->id; + + log_debug(2, "%s discovery create conn\n", __FUNCTION__); + rc = ipc->create_conn(t->handle, session->id, conn->id, &conn->id); + if (rc) { + log_error("Could not create connection (err %d)", rc); + goto disconnect; + } + + log_debug(2, "%s discovery bind conn\n", __FUNCTION__); + if (ipc->bind_conn(t->handle, session->id, conn->id, + conn->transport_ep_handle, (conn->id == 0), &rc) || + rc) { + log_error("Could not bind conn %d:%d to session %d, " + "(err %d)", session->id, conn->id, + session->id, rc); + goto disconnect; + } + + /* all set */ + return 0; + +disconnect: + t->template->ep_disconnect(conn); + + if (session->id != -1 && + iscsi_sysfs_session_has_leadconn(session->id)) { + if (ipc->destroy_conn(session->t->handle, session->id, + conn->id)) + log_error("Could not safely destroy connection %d:%d", + session->id, conn->id); + } + + if (session->id != -1) { + if (ipc->destroy_session(session->t->handle, session->id)) + log_error("Could not safely destroy session %d", + session->id); + session->id = -1; + } + +close_ipc: + if (conn->socket_fd >= 0) { + ipc->ctldev_close(); + conn->socket_fd = -1; + } +fail: + log_error("Connection to discovery portal %s failed (err %d)", + conn->host, rc); + return rc; +} + +static struct iscsi_ev_context * +iscsi_ev_context_get(struct iscsi_conn *conn, int ev_size) +{ + log_debug(2, "%s: ev_size %d\n", __FUNCTION__, ev_size); + + ipc_ev_context.data = calloc(1, ev_size); + if (!ipc_ev_context.data) + return NULL; + + return &ipc_ev_context; +} + +static void iscsi_ev_context_put(struct iscsi_ev_context *ev_context) +{ + if (ev_context->data) + free(ev_context->data); + ev_context->data = NULL; +} + +static int iscsi_sched_ev_context(struct iscsi_ev_context *ev_context, + struct iscsi_conn *conn, unsigned long tmo, + int event) +{ + if (event == EV_CONN_RECV_PDU) { + conn->recv_context = ev_context; + return 0; } + return -EIO; +} + +static struct iscsi_ipc_ev_clbk ipc_clbk = { + .get_ev_context = iscsi_ev_context_get, + .put_ev_context = iscsi_ev_context_put, + .sched_ev_context = iscsi_sched_ev_context, +}; + +static int iscsi_create_session(struct iscsi_session *session, + struct iscsi_sendtargets_config *config, + char *data, unsigned int data_len) +{ + struct iscsi_conn *conn = &session->conn[0]; + int rc, login_delay = 0; + uint8_t status_class = 0, status_detail = 0; + unsigned int login_failures = 0; + char serv[NI_MAXSERV]; + struct iscsi_transport *t = session->t; + set_address: /* * copy the saved address to the session, * undoing any temporary redirect */ - session->conn[0].saddr = ss; + conn->saddr = conn->failback_saddr; reconnect: - + /* fix decrement and test */ if (--session->reopen_cnt < 0) { - log_error("connection login retries (reopen_max %d) exceeded", + log_error("connection login retries (reopen_max) %d exceeded", config->reopen_max); - rc = 1; - goto free_sendtargets; + goto login_failed; } redirect_reconnect: - - iscsi_io_disconnect(&session->conn[0]); - session->cmdsn = 1; session->itt = 1; session->portal_group_tag = PORTAL_GROUP_TAG_UNKNOWN; + /* + * On reconnect, just destroy the kernel structs and start over. + */ + iscsi_destroy_session(session); + /* slowly back off the frequency of login attempts */ if (login_failures == 0) login_delay = 0; @@ -1257,38 +1267,33 @@ redirect_reconnect: else login_delay = 60; /* after 2 minutes, try once a minute */ + getnameinfo((struct sockaddr *) &conn->saddr, + sizeof(conn->saddr), conn->host, + sizeof(conn->host), serv, sizeof(serv), + NI_NUMERICHOST|NI_NUMERICSERV); + if (login_delay) { - log_debug(4, "discovery session to %s:%d sleeping for %d " + log_debug(4, "discovery session to %s:%s sleeping for %d " "seconds before next login attempt", - drec->address, drec->port, login_delay); + conn->host, serv, login_delay); sleep(login_delay); } - - getnameinfo((struct sockaddr *) &session->conn[0].saddr, - sizeof(session->conn[0].saddr), host, - sizeof(host), serv, sizeof(serv), - NI_NUMERICHOST|NI_NUMERICSERV); - - if (!iscsi_io_connect(&session->conn[0])) { - log_error("connection to discovery address %s " - "failed", host); - + if (iscsi_create_leading_conn(session)) { login_failures++; - /* If a temporary redirect sent us to something unreachable, - * we want to go back to the original IP address, so make sure - * we reset the session's IP. - */ - goto set_address; + goto reconnect; } - log_debug(1, "connected to discovery address %s", host); + log_debug(1, "connected to discovery address %s", conn->host); - log_debug(4, "discovery session to %s:%d starting iSCSI login on fd %d", - drec->address, drec->port, session->conn[0].socket_fd); + log_debug(4, "discovery session to %s:%s starting iSCSI login", + conn->host, serv); - /* In case of discovery, we using socket's descriptor as ctrl. */ - session->ctrl_fd = session->conn[0].socket_fd; - session->conn[0].session = session; + /* + * Need to re-init settings because a previous login could + * have set them to what was negotiated for. + */ + iscsi_copy_operational_params(&session->conn[0], &config->session_conf, + &config->conn_conf); status_class = 0; status_detail = 0; @@ -1305,8 +1310,7 @@ redirect_reconnect: case LOGIN_IO_ERROR: case LOGIN_REDIRECTION_FAILED: /* try again */ - log_warning("retrying discovery login to %s", host); - iscsi_io_disconnect(&session->conn[0]); + log_warning("retrying discovery login to %s", conn->host); login_failures++; goto set_address; @@ -1316,16 +1320,15 @@ redirect_reconnect: case LOGIN_AUTHENTICATION_FAILED: case LOGIN_VERSION_MISMATCH: case LOGIN_INVALID_PDU: - log_error("discovery login to %s failed, giving up", host); - iscsi_io_disconnect(&session->conn[0]); - rc = 1; - goto free_sendtargets; + log_error("discovery login to %s failed, giving up %d", + conn->host, rc); + goto login_failed; } /* check the login status */ switch (status_class) { case ISCSI_STATUS_CLS_SUCCESS: - log_debug(4, "discovery login success to %s", host); + log_debug(4, "discovery login success to %s", conn->host); login_failures = 0; break; case ISCSI_STATUS_CLS_REDIRECT: @@ -1337,14 +1340,16 @@ redirect_reconnect: case ISCSI_LOGIN_STATUS_TGT_MOVED_TEMP: log_warning( "discovery login temporarily redirected to " - "%s port %s", host, serv); + "%s port %s", conn->host, serv); goto redirect_reconnect; case ISCSI_LOGIN_STATUS_TGT_MOVED_PERM: log_warning( "discovery login permanently redirected to " - "%s port %s", host, serv); + "%s port %s", conn->host, serv); /* make the new address permanent */ - ss = session->conn[0].saddr; + memset(&conn->failback_saddr, 0, + sizeof(struct sockaddr_storage)); + conn->failback_saddr = conn->saddr; goto redirect_reconnect; default: log_error( @@ -1358,29 +1363,121 @@ redirect_reconnect: log_error( "discovery login to %s rejected: " "initiator error (%02x/%02x), non-retryable, giving up", - host, status_class, status_detail); - iscsi_io_disconnect(&session->conn[0]); - rc = 1; - goto free_sendtargets; + conn->host, status_class, status_detail); + goto login_failed; case ISCSI_STATUS_CLS_TARGET_ERR: log_error( "discovery login to %s rejected: " "target error (%02x/%02x)", - host, status_class, status_detail); - iscsi_io_disconnect(&session->conn[0]); + conn->host, status_class, status_detail); login_failures++; goto reconnect; default: log_error( "discovery login to %s failed, response " "with unknown status class 0x%x, detail 0x%x", - host, + conn->host, status_class, status_detail); - iscsi_io_disconnect(&session->conn[0]); login_failures++; goto reconnect; } + if (!(t->caps & CAP_TEXT_NEGO)) + return 0; + + log_debug(2, "%s discovery set params\n", __FUNCTION__); + rc = iscsi_session_set_params(conn); + if (rc) { + log_error("Could not set iscsi params for conn %d:%d (err " + "%d)\n", session->id, conn->id, rc); + goto login_failed; + } + + log_debug(2, "%s discovery start conn\n", __FUNCTION__); + if (ipc->start_conn(t->handle, session->id, conn->id, &rc) || rc) { + log_error("Cannot start conn %d:%d (err %d)", + session->id, conn->id, rc); + goto login_failed; + } + + return 0; + +login_failed: + iscsi_destroy_session(session); + return ENOTCONN; +} + +int discovery_sendtargets(void *fndata, struct iface_rec *iface, + struct list_head *rec_list) +{ + discovery_rec_t *drec = fndata; + iscsi_session_t *session; + struct pollfd pfd; + struct iscsi_hdr pdu_buffer; + struct iscsi_hdr *pdu = &pdu_buffer; + char *data = NULL; + int active = 0, valid_text = 0; + struct timeval connection_timer; + int timeout; + int rc; + struct str_buffer sendtargets; + unsigned int data_len; + struct iscsi_sendtargets_config *config = &drec->u.sendtargets; + + /* initial setup */ + log_debug(1, "starting sendtargets discovery, address %s:%d, ", + drec->address, drec->port); + memset(&pdu_buffer, 0, sizeof (pdu_buffer)); + iscsi_timer_clear(&connection_timer); + + /* allocate a new session, and initialize default values */ + session = iscsi_alloc_session(config, iface); + if (session == NULL) { + log_error("Discovery process to %s:%d failed to " + "create a discovery session.", + drec->address, drec->port); + return 1; + } + + ipc_ev_context.conn = &session->conn[0]; + ipc_register_ev_callback(&ipc_clbk); + + log_debug(4, "sendtargets discovery to %s:%d using " + "isid 0x%02x%02x%02x%02x%02x%02x", + drec->address, drec->port, session->isid[0], + session->isid[1], session->isid[2], session->isid[3], + session->isid[4], session->isid[5]); + + /* allocate data buffers for SendTargets data */ + data = malloc(session->conn[0].max_recv_dlength); + if (!data) { + rc = 1; + goto free_session; + } + data_len = session->conn[0].max_recv_dlength; + + str_init_buffer(&sendtargets, 0); + + /* resolve the DiscoveryAddress to an IP address */ + if (iscsi_setup_portal(&session->conn[0], drec->address, + drec->port)) { + log_error("cannot resolve host name %s", drec->address); + rc = 1; + goto free_sendtargets; + } + + log_debug(4, "discovery timeouts: login %d, reopen_cnt %d, auth %d.", + session->conn[0].login_timeout, session->reopen_cnt, + session->conn[0].auth_timeout); + +reconnect: + rc = iscsi_create_session(session, &drec->u.sendtargets, + data, data_len); + if (rc) { + rc = 1; + goto free_sendtargets; + } + /* reinitialize */ str_truncate_buffer(&sendtargets, 0); @@ -1391,7 +1488,7 @@ redirect_reconnect: active = 1; /* set timeouts */ - set_timer(&connection_timer, session->conn[0].active_timeout); + iscsi_timer_set(&connection_timer, session->conn[0].active_timeout); /* prepare to poll */ memset(&pfd, 0, sizeof (pfd)); @@ -1399,7 +1496,7 @@ redirect_reconnect: pfd.events = POLLIN | POLLPRI; repoll: - timeout = msecs_until(&connection_timer); + timeout = iscsi_timer_msecs_until(&connection_timer); /* block until we receive a PDU, a TCP FIN, a TCP RST, * or a timeout */ @@ -1416,30 +1513,29 @@ repoll: "discovery process to %s:%d returned from poll, rc %d", drec->address, drec->port, rc); - if (timer_expired(&connection_timer)) { - log_warning("discovery session to %s:%d session " - "logout, connection timer expired", + if (iscsi_timer_expired(&connection_timer)) { + log_warning("Discovery session to %s:%d timed out.", drec->address, drec->port); - iscsi_logout_and_disconnect(session); rc = 1; - goto free_sendtargets; + goto reconnect; } if (rc > 0) { if (pfd.revents & (POLLIN | POLLPRI)) { - timeout = msecs_until(&connection_timer); + timeout = iscsi_timer_msecs_until(&connection_timer); - memset(data, 0, data_len); - if (!iscsi_io_recv_pdu(&session->conn[0], - pdu, ISCSI_DIGEST_NONE, data, - data_len, ISCSI_DIGEST_NONE, - timeout)) { + rc = iscsi_io_recv_pdu(&session->conn[0], + pdu, ISCSI_DIGEST_NONE, data, + data_len, ISCSI_DIGEST_NONE, + timeout); + if (rc == -EAGAIN) + goto repoll; + else if (rc < 0) { log_debug(1, "discovery session to " "%s:%d failed to recv a PDU " "response, terminating", drec->address, drec->port); - iscsi_io_disconnect(&session->conn[0]); rc = 1; goto free_sendtargets; } @@ -1449,14 +1545,13 @@ repoll: */ rc = process_recvd_pdu(pdu, drec, rec_list, session, &sendtargets, - default_port, &active, &valid_text, data); if (rc == DISCOVERY_NEED_RECONNECT) goto reconnect; /* reset timers after receiving a PDU */ if (active) { - set_timer(&connection_timer, + iscsi_timer_set(&connection_timer, session->conn[0].active_timeout); goto repoll; } @@ -1466,7 +1561,6 @@ repoll: log_warning("discovery session to %s:%d " "terminating after hangup", drec->address, drec->port); - iscsi_io_disconnect(&session->conn[0]); rc = 1; goto free_sendtargets; } @@ -1483,18 +1577,9 @@ repoll: goto reconnect; } } else if (rc < 0) { - if (errno == EINTR) { - /* if we got SIGHUP, reconnect and rediscover */ - if (rediscover) { - rediscover = 0; - log_debug(1, "rediscovery requested"); - goto reconnect; - } - } else { - log_error("poll error"); - rc = 1; - goto free_sendtargets; - } + log_error("poll error"); + rc = 1; + goto free_sendtargets; } log_debug(1, "discovery process to %s:%d exiting", @@ -1504,8 +1589,9 @@ repoll: free_sendtargets: str_free_buffer(&sendtargets); free(data); + iscsi_destroy_session(session); free_session: - free(session); + iscsi_free_session(session); return rc; } diff --git a/usr/idbm.c b/usr/idbm.c index 67810ab..cd93e9f 100644 --- a/usr/idbm.c +++ b/usr/idbm.c @@ -179,7 +179,7 @@ idbm_recinfo_discovery(discovery_rec_t *r, recinfo_t *ri) u.sendtargets.conn_timeo.active_timeout, IDBM_SHOW, num, 1); __recinfo_int(DISC_ST_MAX_RECV_DLEN, ri, r, - u.sendtargets.iscsi.MaxRecvDataSegmentLength, + u.sendtargets.conn_conf.MaxRecvDataSegmentLength, IDBM_SHOW, num, 1); break; case DISCOVERY_TYPE_ISNS: @@ -425,6 +425,31 @@ void idbm_print(int type, void *rec, int show, FILE *f) free(info); } +static void +idbm_setup_session_defaults(struct iscsi_session_operational_config *conf) +{ + conf->InitialR2T = 0; + conf->ImmediateData = 1; + conf->FirstBurstLength = DEF_INI_FIRST_BURST_LEN; + conf->MaxBurstLength = DEF_INI_MAX_BURST_LEN; + conf->DefaultTime2Wait = ISCSI_DEF_TIME2WAIT; + conf->DefaultTime2Retain = 0; + conf->MaxConnections = 1; + conf->MaxOutstandingR2T = 1; + conf->ERL = 0; + conf->FastAbort = 1; +} + +static void idbm_setup_conn_defaults(struct iscsi_conn_operational_config *conf) +{ + conf->MaxXmitDataSegmentLength = 0; + conf->MaxRecvDataSegmentLength = DEF_INI_MAX_RECV_SEG_LEN; + conf->HeaderDigest = CONFIG_DIGEST_NEVER; + conf->DataDigest = CONFIG_DIGEST_NEVER; + conf->IFMarker = 0; + conf->OFMarker = 0; +} + static void idbm_discovery_setup_defaults(discovery_rec_t *rec, discovery_type_e type) { @@ -443,7 +468,10 @@ idbm_discovery_setup_defaults(discovery_rec_t *rec, discovery_type_e type) rec->u.sendtargets.conn_timeo.login_timeout=15; rec->u.sendtargets.conn_timeo.auth_timeout = 45; rec->u.sendtargets.conn_timeo.active_timeout=30; - rec->u.sendtargets.iscsi.MaxRecvDataSegmentLength = + idbm_setup_session_defaults(&rec->u.sendtargets.session_conf); + idbm_setup_conn_defaults(&rec->u.sendtargets.conn_conf); + /* override def setting */ + rec->u.sendtargets.conn_conf.MaxRecvDataSegmentLength = DEF_INI_DISC_MAX_RECV_SEG_LEN; break; case DISCOVERY_TYPE_SLP: @@ -2362,16 +2390,7 @@ void idbm_node_setup_defaults(node_rec_t *rec) rec->session.err_timeo.tgt_reset_timeout = DEF_TGT_RESET_TIMEO; rec->session.err_timeo.host_reset_timeout = DEF_HOST_RESET_TIMEO; rec->session.timeo.replacement_timeout = DEF_REPLACEMENT_TIMEO; - rec->session.iscsi.InitialR2T = 0; - rec->session.iscsi.ImmediateData = 1; - rec->session.iscsi.FirstBurstLength = DEF_INI_FIRST_BURST_LEN; - rec->session.iscsi.MaxBurstLength = DEF_INI_MAX_BURST_LEN; - rec->session.iscsi.DefaultTime2Wait = ISCSI_DEF_TIME2WAIT; - rec->session.iscsi.DefaultTime2Retain = 0; - rec->session.iscsi.MaxConnections = 1; - rec->session.iscsi.MaxOutstandingR2T = 1; - rec->session.iscsi.ERL = 0; - rec->session.iscsi.FastAbort = 1; + idbm_setup_session_defaults(&rec->session.iscsi); for (i=0; iconn[i].startup = ISCSI_STARTUP_MANUAL; @@ -2385,13 +2404,7 @@ void idbm_node_setup_defaults(node_rec_t *rec) rec->conn[i].timeo.noop_out_interval = DEF_NOOP_OUT_INTERVAL; rec->conn[i].timeo.noop_out_timeout = DEF_NOOP_OUT_TIMEO; - rec->conn[i].iscsi.MaxXmitDataSegmentLength = 0; - rec->conn[i].iscsi.MaxRecvDataSegmentLength = - DEF_INI_MAX_RECV_SEG_LEN; - rec->conn[i].iscsi.HeaderDigest = CONFIG_DIGEST_NEVER; - rec->conn[i].iscsi.DataDigest = CONFIG_DIGEST_NEVER; - rec->conn[i].iscsi.IFMarker = 0; - rec->conn[i].iscsi.OFMarker = 0; + idbm_setup_conn_defaults(&rec->conn[i].iscsi); } iface_setup_defaults(&rec->iface); diff --git a/usr/initiator.c b/usr/initiator.c index 70c873b..81e407f 100644 --- a/usr/initiator.c +++ b/usr/initiator.c @@ -53,31 +53,17 @@ #define PROC_DIR "/proc" static void iscsi_login_timedout(void *data); +static int iscsi_sched_ev_context(struct iscsi_ev_context *ev_context, + struct iscsi_conn *conn, unsigned long tmo, + int event); -/* - * calculate parameter's padding - */ -static unsigned int -__padding(unsigned int param) -{ - int pad; - - pad = param & 3; - if (pad) { - pad = 4 - pad; - log_debug(1, "parameter's value %d padded to %d bytes\n", - param, param + pad); - } - return param + pad; -} - -static int iscsi_conn_context_alloc(iscsi_conn_t *conn) +static int iscsi_ev_context_alloc(iscsi_conn_t *conn) { int i; for (i = 0; i < CONTEXT_POOL_MAX; i++) { conn->context_pool[i] = calloc(1, - sizeof(struct iscsi_conn_context) + + sizeof(struct iscsi_ev_context) + ipc->ctldev_bufmax); if (!conn->context_pool[i]) { int j; @@ -91,7 +77,7 @@ static int iscsi_conn_context_alloc(iscsi_conn_t *conn) return 0; } -static void iscsi_conn_context_free(iscsi_conn_t *conn) +static void iscsi_ev_context_free(iscsi_conn_t *conn) { int i; @@ -107,10 +93,10 @@ static void iscsi_conn_context_free(iscsi_conn_t *conn) } } -struct iscsi_conn_context *iscsi_conn_context_get(iscsi_conn_t *conn, - int ev_size) +static struct iscsi_ev_context * +iscsi_ev_context_get(iscsi_conn_t *conn, int ev_size) { - struct iscsi_conn_context *conn_context; + struct iscsi_ev_context *ev_context; int i; if (ev_size > ipc->ctldev_bufmax) @@ -121,26 +107,26 @@ struct iscsi_conn_context *iscsi_conn_context_get(iscsi_conn_t *conn, continue; if (!conn->context_pool[i]->allocated) { - conn_context = conn->context_pool[i]; + ev_context = conn->context_pool[i]; - memset(&conn_context->actor, 0, + memset(&ev_context->actor, 0, sizeof(struct actor)); - conn_context->allocated = 1; + ev_context->allocated = 1; /* some callers abuse this pointer */ - conn_context->data = (void *)conn_context + - sizeof(struct iscsi_conn_context); - log_debug(7, "get conn context %p", - &conn_context->actor); - return conn_context; + ev_context->data = (void *)ev_context + + sizeof(struct iscsi_ev_context); + log_debug(7, "get ev context %p", + &ev_context->actor); + return ev_context; } } return NULL; } -void iscsi_conn_context_put(struct iscsi_conn_context *conn_context) +static void iscsi_ev_context_put(struct iscsi_ev_context *ev_context) { - log_debug(7, "put conn context %p", &conn_context->actor); - conn_context->allocated = 0; + log_debug(7, "put ev context %p", &ev_context->actor); + ev_context->allocated = 0; } static void session_online_devs(int host_no, int sid) @@ -250,183 +236,6 @@ __check_iscsi_status_class(iscsi_session_t *session, int cid, return CONN_LOGIN_FAILED; } -static void -__setup_authentication(iscsi_session_t *session, - struct iscsi_auth_config *auth_cfg) -{ - /* if we have any incoming credentials, we insist on authenticating - * the target or not logging in at all - */ - if (auth_cfg->username_in[0] - || auth_cfg->password_in_length) { - /* sanity check the config */ - if (auth_cfg->password_length == 0) { - log_debug(1, - "node record has incoming " - "authentication credentials but has no outgoing " - "credentials configured, exiting"); - return; - } - session->bidirectional_auth = 1; - } else { - /* no or 1-way authentication */ - session->bidirectional_auth = 0; - } - - /* copy in whatever credentials we have */ - strlcpy(session->username, auth_cfg->username, - sizeof (session->username)); - session->username[sizeof (session->username) - 1] = '\0'; - if ((session->password_length = auth_cfg->password_length)) - memcpy(session->password, auth_cfg->password, - session->password_length); - - strlcpy(session->username_in, auth_cfg->username_in, - sizeof (session->username_in)); - session->username_in[sizeof (session->username_in) - 1] = '\0'; - if ((session->password_in_length = - auth_cfg->password_in_length)) - memcpy(session->password_in, auth_cfg->password_in, - session->password_in_length); - - if (session->password_length || session->password_in_length) { - /* setup the auth buffers */ - session->auth_buffers[0].address = &session->auth_client_block; - session->auth_buffers[0].length = - sizeof (session->auth_client_block); - session->auth_buffers[1].address = - &session->auth_recv_string_block; - session->auth_buffers[1].length = - sizeof (session->auth_recv_string_block); - - session->auth_buffers[2].address = - &session->auth_send_string_block; - session->auth_buffers[2].length = - sizeof (session->auth_send_string_block); - - session->auth_buffers[3].address = - &session->auth_recv_binary_block; - session->auth_buffers[3].length = - sizeof (session->auth_recv_binary_block); - - session->auth_buffers[4].address = - &session->auth_send_binary_block; - session->auth_buffers[4].length = - sizeof (session->auth_send_binary_block); - - session->num_auth_buffers = 5; - log_debug(6, "authentication setup complete..."); - } else { - session->num_auth_buffers = 0; - log_debug(6, "no authentication configured..."); - } -} - -static int -setup_portal(iscsi_conn_t *conn, conn_rec_t *conn_rec) -{ - char port[NI_MAXSERV]; - - sprintf(port, "%d", conn_rec->port); - if (resolve_address(conn_rec->address, port, &conn->saddr)) { - log_error("cannot resolve host name %s", - conn_rec->address); - return EINVAL; - } - conn->failback_saddr = conn->saddr; - - getnameinfo((struct sockaddr *)&conn->saddr, sizeof(conn->saddr), - conn->host, sizeof(conn->host), NULL, 0, NI_NUMERICHOST); - log_debug(4, "resolved %s to %s", conn_rec->address, conn->host); - return 0; -} - -static void -iscsi_copy_operational_params(iscsi_conn_t *conn) -{ - iscsi_session_t *session = conn->session; - conn_rec_t *conn_rec = &session->nrec.conn[conn->id]; - node_rec_t *rec = &session->nrec; - - conn->hdrdgst_en = conn_rec->iscsi.HeaderDigest; - conn->datadgst_en = conn_rec->iscsi.DataDigest; - - conn->max_recv_dlength = - __padding(conn_rec->iscsi.MaxRecvDataSegmentLength); - if (conn->max_recv_dlength < ISCSI_MIN_MAX_RECV_SEG_LEN || - conn->max_recv_dlength > ISCSI_MAX_MAX_RECV_SEG_LEN) { - log_error("Invalid iscsi.MaxRecvDataSegmentLength. Must be " - "within %u and %u. Setting to %u\n", - ISCSI_MIN_MAX_RECV_SEG_LEN, - ISCSI_MAX_MAX_RECV_SEG_LEN, - DEF_INI_MAX_RECV_SEG_LEN); - conn_rec->iscsi.MaxRecvDataSegmentLength = - DEF_INI_MAX_RECV_SEG_LEN; - conn->max_recv_dlength = DEF_INI_MAX_RECV_SEG_LEN; - } - - /* zero indicates to use the target's value */ - conn->max_xmit_dlength = - __padding(conn_rec->iscsi.MaxXmitDataSegmentLength); - if (conn->max_xmit_dlength == 0) - conn->max_xmit_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN; - if (conn->max_xmit_dlength < ISCSI_MIN_MAX_RECV_SEG_LEN || - conn->max_xmit_dlength > ISCSI_MAX_MAX_RECV_SEG_LEN) { - log_error("Invalid iscsi.MaxXmitDataSegmentLength. Must be " - "within %u and %u. Setting to %u\n", - ISCSI_MIN_MAX_RECV_SEG_LEN, - ISCSI_MAX_MAX_RECV_SEG_LEN, - DEF_INI_MAX_RECV_SEG_LEN); - conn_rec->iscsi.MaxXmitDataSegmentLength = - DEF_INI_MAX_RECV_SEG_LEN; - conn->max_xmit_dlength = DEF_INI_MAX_RECV_SEG_LEN; - } - - /* session's operational parameters */ - session->initial_r2t_en = rec->session.iscsi.InitialR2T; - session->imm_data_en = rec->session.iscsi.ImmediateData; - session->first_burst = __padding(rec->session.iscsi.FirstBurstLength); - /* - * some targets like netapp fail the login if sent bad first_burst - * and max_burst lens, even when immediate data=no and - * initial r2t = Yes, so we always check the user values. - */ - if (session->first_burst < ISCSI_MIN_FIRST_BURST_LEN || - session->first_burst > ISCSI_MAX_FIRST_BURST_LEN) { - log_error("Invalid iscsi.FirstBurstLength of %u. Must be " - "within %u and %u. Setting to %u\n", - session->first_burst, - ISCSI_MIN_FIRST_BURST_LEN, - ISCSI_MAX_FIRST_BURST_LEN, - DEF_INI_FIRST_BURST_LEN); - rec->session.iscsi.FirstBurstLength = DEF_INI_FIRST_BURST_LEN; - session->first_burst = DEF_INI_FIRST_BURST_LEN; - } - - session->max_burst = __padding(rec->session.iscsi.MaxBurstLength); - if (session->max_burst < ISCSI_MIN_MAX_BURST_LEN || - session->max_burst > ISCSI_MAX_MAX_BURST_LEN) { - log_error("Invalid iscsi.MaxBurstLength of %u. Must be " - "within %u and %u. Setting to %u\n", - session->max_burst, ISCSI_MIN_MAX_BURST_LEN, - ISCSI_MAX_MAX_BURST_LEN, DEF_INI_MAX_BURST_LEN); - rec->session.iscsi.MaxBurstLength = DEF_INI_MAX_BURST_LEN; - session->max_burst = DEF_INI_MAX_BURST_LEN; - } - - if (session->first_burst > session->max_burst) { - log_error("Invalid iscsi.FirstBurstLength of %u. Must be " - "less than iscsi.MaxBurstLength. Setting to %u\n", - session->first_burst, session->max_burst); - rec->session.iscsi.FirstBurstLength = session->max_burst; - session->first_burst = session->max_burst; - } - - session->def_time2wait = rec->session.iscsi.DefaultTime2Wait; - session->def_time2retain = rec->session.iscsi.DefaultTime2Retain; - session->erl = rec->session.iscsi.ERL; -} - static int __session_conn_create(iscsi_session_t *session, int cid) { @@ -434,7 +243,7 @@ __session_conn_create(iscsi_session_t *session, int cid) conn_rec_t *conn_rec = &session->nrec.conn[cid]; int err; - if (iscsi_conn_context_alloc(conn)) { + if (iscsi_ev_context_alloc(conn)) { log_error("cannot allocate context_pool for conn cid %d", cid); return ENOMEM; } @@ -486,14 +295,15 @@ __session_conn_create(iscsi_session_t *session, int cid) conn->noop_out_interval = DEF_NOOP_OUT_INTERVAL; } - iscsi_copy_operational_params(conn); + iscsi_copy_operational_params(conn, &session->nrec.session.iscsi, + &conn_rec->iscsi); /* TCP options */ conn->tcp_window_size = conn_rec->tcp.window_size; /* FIXME: type_of_service */ /* resolve the string address to an IP address */ - err = setup_portal(conn, conn_rec); + err = iscsi_setup_portal(conn, conn_rec->address, conn_rec->port); if (err) return err; return 0; @@ -506,7 +316,7 @@ session_release(iscsi_session_t *session) if (session->target_alias) free(session->target_alias); - iscsi_conn_context_free(&session->conn[0]); + iscsi_ev_context_free(&session->conn[0]); free(session); } @@ -524,11 +334,10 @@ __session_create(node_rec_t *rec, struct iscsi_transport *t) log_debug(2, "Allocted session %p", session); INIT_LIST_HEAD(&session->list); - /* opened at daemon load time (iscsid.c) */ - session->ctrl_fd = control_fd; session->t = t; session->reopen_qtask.mgmt_ipc_fd = -1; session->id = -1; + session->use_ipc = 1; /* save node record. we might need it for redirection */ memcpy(&session->nrec, rec, sizeof(node_rec_t)); @@ -570,7 +379,7 @@ __session_create(node_rec_t *rec, struct iscsi_transport *t) session->isid[5] = 0; /* setup authentication variables for the session*/ - __setup_authentication(session, &rec->session.auth); + iscsi_setup_authentication(session, &rec->session.auth); session->param_mask = ~0ULL; if (!(t->caps & CAP_MULTI_R2T)) @@ -601,18 +410,18 @@ __session_create(node_rec_t *rec, struct iscsi_transport *t) static void iscsi_flush_context_pool(struct iscsi_session *session) { - struct iscsi_conn_context *conn_context; + struct iscsi_ev_context *ev_context; struct iscsi_conn *conn = &session->conn[0]; int i; for (i = 0; i < CONTEXT_POOL_MAX; i++) { - conn_context = conn->context_pool[i]; - if (!conn_context) + ev_context = conn->context_pool[i]; + if (!ev_context) continue; - if (conn_context->allocated) { + if (ev_context->allocated) { actor_delete(&(conn->context_pool[i]->actor)); - iscsi_conn_context_put(conn_context); + iscsi_ev_context_put(ev_context); } } } @@ -709,17 +518,17 @@ queue_delayed_reopen(queue_task_t *qtask, int delay) static int iscsi_conn_connect(struct iscsi_conn *conn, queue_task_t *qtask) { - struct iscsi_conn_context *conn_context; + struct iscsi_ev_context *ev_context; int rc; - conn_context = iscsi_conn_context_get(conn, 0); - if (!conn_context) { + ev_context = iscsi_ev_context_get(conn, 0); + if (!ev_context) { /* while reopening the recv pool should be full */ log_error("BUG: __session_conn_reopen could not get conn " "context for recv."); return ENOMEM; } - conn_context->data = qtask; + ev_context->data = qtask; rc = conn->session->t->template->ep_connect(conn, 1); if (rc < 0 && errno != EINPROGRESS) { @@ -732,11 +541,11 @@ static int iscsi_conn_connect(struct iscsi_conn *conn, queue_task_t *qtask) log_error("cannot make a connection to %s:%s (%d,%d)", conn->host, serv, rc, errno); - iscsi_conn_context_put(conn_context); + iscsi_ev_context_put(ev_context); return ENOTCONN; } - iscsi_sched_conn_context(conn_context, conn, 0, EV_CONN_POLL); + iscsi_sched_ev_context(ev_context, conn, 0, EV_CONN_POLL); log_debug(3, "Setting login timer %p timeout %d", &conn->login_timer, conn->login_timeout); actor_timer(&conn->login_timer, conn->login_timeout * 1000, @@ -1020,15 +829,15 @@ __conn_error_handle(iscsi_session_t *session, iscsi_conn_t *conn) static void session_conn_error(void *data) { - struct iscsi_conn_context *conn_context = data; - enum iscsi_err error = *(enum iscsi_err *)conn_context->data; - iscsi_conn_t *conn = conn_context->conn; + struct iscsi_ev_context *ev_context = data; + enum iscsi_err error = *(enum iscsi_err *)ev_context->data; + iscsi_conn_t *conn = ev_context->conn; iscsi_session_t *session = conn->session; log_warning("Kernel reported iSCSI connection %d:%d error (%d) " "state (%d)", session->id, conn->id, error, conn->state); - iscsi_conn_context_put(conn_context); + iscsi_ev_context_put(ev_context); switch (error) { case ISCSI_ERR_INVALID_HOST: @@ -1136,17 +945,6 @@ static void conn_send_nop_out(void *data) &conn->nop_out_timer, conn->noop_out_timeout); } -static void -print_param_value(enum iscsi_param param, void *value, int type) -{ - log_debug(3, "set operational parameter %d to:", param); - - if (type == ISCSI_STRING) - log_debug(3, "%s", value ? (char *)value : "NULL"); - else - log_debug(3, "%u", *(uint32_t *)value); -} - void free_initiator(void) { struct iscsi_transport *t; @@ -1188,292 +986,23 @@ static void session_scan_host(struct iscsi_session *session, int hostno, mgmt_ipc_write_rsp(qtask, MGMT_IPC_ERR_INTERNAL); } -static int __iscsi_host_set_param(struct iscsi_transport *t, - int host_no, int param, char *value, - int type) -{ - int rc; - - rc = ipc->set_host_param(t->handle, host_no, param, value, type); - /* 2.6.20 and below returns EINVAL */ - if (rc && rc != -ENOSYS && rc != -EINVAL) { - log_error("can't set operational parameter %d for " - "host %d, retcode %d (%d)", param, host_no, - rc, errno); - return rc; - } - return 0; -} - -mgmt_ipc_err_e iscsi_host_set_param(int host_no, int param, char *value) -{ - struct iscsi_transport *t; - - t = iscsi_sysfs_get_transport_by_hba(host_no); - if (!t) - return MGMT_IPC_ERR_TRANS_FAILURE; - if (__iscsi_host_set_param(t, host_no, param, value, ISCSI_STRING)) - return MGMT_IPC_ERR; - return MGMT_IPC_OK; -} - -#define MAX_SESSION_PARAMS 32 -#define MAX_HOST_PARAMS 3 - static void setup_full_feature_phase(iscsi_conn_t *conn) { iscsi_session_t *session = conn->session; iscsi_login_context_t *c = &conn->login_context; - int i, rc; - uint32_t one = 1, zero = 0; - struct hostparam { - int param; - int type; - void *value; - int set; - } hosttbl[MAX_HOST_PARAMS] = { - { - .param = ISCSI_HOST_PARAM_NETDEV_NAME, - .value = session->nrec.iface.netdev, - .type = ISCSI_STRING, - .set = 1, - }, { - .param = ISCSI_HOST_PARAM_HWADDRESS, - .value = session->nrec.iface.hwaddress, - .type = ISCSI_STRING, - .set = 1, - }, { - .param = ISCSI_HOST_PARAM_INITIATOR_NAME, - .value = session->initiator_name, - .type = ISCSI_STRING, - .set = 0, - }, - }; - struct connparam { - int param; - int type; - void *value; - int conn_only; - } conntbl[MAX_SESSION_PARAMS] = { - { - .param = ISCSI_PARAM_MAX_RECV_DLENGTH, - .value = &conn->max_recv_dlength, - .type = ISCSI_INT, - .conn_only = 0, - }, { - .param = ISCSI_PARAM_MAX_XMIT_DLENGTH, - .value = &conn->max_xmit_dlength, - .type = ISCSI_INT, - .conn_only = 0, - }, { - .param = ISCSI_PARAM_HDRDGST_EN, - .value = &conn->hdrdgst_en, - .type = ISCSI_INT, - .conn_only = 0, - }, { - .param = ISCSI_PARAM_DATADGST_EN, - .value = &conn->datadgst_en, - .type = ISCSI_INT, - .conn_only = 1, - }, { - .param = ISCSI_PARAM_INITIAL_R2T_EN, - .value = &session->initial_r2t_en, - .type = ISCSI_INT, - .conn_only = 0, - }, { - .param = ISCSI_PARAM_MAX_R2T, - .value = &one, /* FIXME: session->max_r2t */ - .type = ISCSI_INT, - .conn_only = 0, - }, { - .param = ISCSI_PARAM_IMM_DATA_EN, - .value = &session->imm_data_en, - .type = ISCSI_INT, - .conn_only = 0, - }, { - .param = ISCSI_PARAM_FIRST_BURST, - .value = &session->first_burst, - .type = ISCSI_INT, - .conn_only = 0, - }, { - .param = ISCSI_PARAM_MAX_BURST, - .value = &session->max_burst, - .type = ISCSI_INT, - .conn_only = 0, - }, { - .param = ISCSI_PARAM_PDU_INORDER_EN, - .value = &session->pdu_inorder_en, - .type = ISCSI_INT, - .conn_only = 0, - }, { - .param =ISCSI_PARAM_DATASEQ_INORDER_EN, - .value = &session->dataseq_inorder_en, - .type = ISCSI_INT, - .conn_only = 0, - }, { - .param = ISCSI_PARAM_ERL, - .value = &zero, /* FIXME: session->erl */ - .type = ISCSI_INT, - .conn_only = 0, - }, { - .param = ISCSI_PARAM_IFMARKER_EN, - .value = &zero,/* FIXME: session->ifmarker_en */ - .type = ISCSI_INT, - .conn_only = 0, - }, { - .param = ISCSI_PARAM_OFMARKER_EN, - .value = &zero,/* FIXME: session->ofmarker_en */ - .type = ISCSI_INT, - .conn_only = 0, - }, { - .param = ISCSI_PARAM_EXP_STATSN, - .value = &conn->exp_statsn, - .type = ISCSI_INT, - .conn_only = 1, - }, { - .param = ISCSI_PARAM_TARGET_NAME, - .conn_only = 0, - .type = ISCSI_STRING, - .value = session->target_name, - }, { - .param = ISCSI_PARAM_TPGT, - .value = &session->portal_group_tag, - .type = ISCSI_INT, - .conn_only = 0, - }, { - .param = ISCSI_PARAM_PERSISTENT_ADDRESS, - .value = session->nrec.conn[conn->id].address, - .type = ISCSI_STRING, - .conn_only = 1, - }, { - .param = ISCSI_PARAM_PERSISTENT_PORT, - .value = &session->nrec.conn[conn->id].port, - .type = ISCSI_INT, - .conn_only = 1, - }, { - .param = ISCSI_PARAM_SESS_RECOVERY_TMO, - .value = &session->replacement_timeout, - .type = ISCSI_INT, - .conn_only = 0, - }, { - .param = ISCSI_PARAM_USERNAME, - .value = session->username, - .type = ISCSI_STRING, - .conn_only = 0, - }, { - .param = ISCSI_PARAM_USERNAME_IN, - .value = session->username_in, - .type = ISCSI_STRING, - .conn_only = 0, - }, { - .param = ISCSI_PARAM_PASSWORD, - .value = session->password, - .type = ISCSI_STRING, - .conn_only = 0, - }, { - .param = ISCSI_PARAM_PASSWORD_IN, - .value = session->password_in, - .type = ISCSI_STRING, - .conn_only = 0, - }, { - .param = ISCSI_PARAM_FAST_ABORT, - .value = &session->fast_abort, - .type = ISCSI_INT, - .conn_only = 0, - }, { - .param = ISCSI_PARAM_ABORT_TMO, - .value = &session->abort_timeout, - .type = ISCSI_INT, - .conn_only = 0, - }, { - .param = ISCSI_PARAM_LU_RESET_TMO, - .value = &session->lu_reset_timeout, - .type = ISCSI_INT, - .conn_only = 0, - }, { - .param = ISCSI_PARAM_TGT_RESET_TMO, - .value = &session->tgt_reset_timeout, - .type = ISCSI_INT, - .conn_only = 0, - }, { - .param = ISCSI_PARAM_PING_TMO, - .value = &conn->noop_out_timeout, - .type = ISCSI_INT, - .conn_only = 1, - }, { - .param = ISCSI_PARAM_RECV_TMO, - .value = &conn->noop_out_interval, - .type = ISCSI_INT, - .conn_only = 1, - }, { - .param = ISCSI_PARAM_IFACE_NAME, - .value = session->nrec.iface.name, - .type = ISCSI_STRING, - }, { - .param = ISCSI_PARAM_INITIATOR_NAME, - .value = session->initiator_name, - .type = ISCSI_STRING, - }, - }; + int rc; actor_delete(&conn->login_timer); - /* Entered full-feature phase! */ - for (i = 0; i < MAX_SESSION_PARAMS; i++) { - if (conn->id != 0 && !conntbl[i].conn_only) - continue; - - if (!(session->param_mask & (1ULL << conntbl[i].param))) - continue; - - rc = ipc->set_param(session->t->handle, session->id, - conn->id, conntbl[i].param, conntbl[i].value, - conntbl[i].type); - if (rc && rc != -ENOSYS) { - log_error("can't set operational parameter %d for " - "connection %d:%d, retcode %d (%d)", - conntbl[i].param, session->id, conn->id, - rc, errno); - - iscsi_login_eh(conn, c->qtask, - MGMT_IPC_ERR_LOGIN_FAILURE); - return; - } - if (rc == -ENOSYS) { - switch (conntbl[i].param) { - case ISCSI_PARAM_PING_TMO: - /* - * older kernels may not support nops - * in kernel - */ - conn->userspace_nop = 1; - break; - case ISCSI_PARAM_INITIATOR_NAME: - /* use host level one instead */ - hosttbl[ISCSI_HOST_PARAM_INITIATOR_NAME].set = 1; - break; - } - } - - print_param_value(conntbl[i].param, conntbl[i].value, - conntbl[i].type); + if (iscsi_session_set_params(conn)) { + iscsi_login_eh(conn, c->qtask, MGMT_IPC_ERR_LOGIN_FAILURE); + return; } - for (i = 0; i < MAX_HOST_PARAMS; i++) { - if (!hosttbl[i].set) - continue; - - if (__iscsi_host_set_param(session->t, session->hostno, - hosttbl[i].param, hosttbl[i].value, - hosttbl[i].type)) { - iscsi_login_eh(conn, c->qtask, - MGMT_IPC_ERR_LOGIN_FAILURE); - return; - } - - print_param_value(hosttbl[i].param, hosttbl[i].value, - hosttbl[i].type); + if (iscsi_host_set_params(session)) { + iscsi_login_eh(conn, c->qtask, MGMT_IPC_ERR_LOGIN_FAILURE); + return; } if (ipc->start_conn(session->t->handle, session->id, conn->id, @@ -1527,10 +1056,10 @@ setup_full_feature_phase(iscsi_conn_t *conn) static void iscsi_logout_timedout(void *data) { - struct iscsi_conn_context *conn_context = data; - struct iscsi_conn *conn = conn_context->conn; + struct iscsi_ev_context *ev_context = data; + struct iscsi_conn *conn = ev_context->conn; - iscsi_conn_context_put(conn_context); + iscsi_ev_context_put(ev_context); /* * assume we were in STATE_IN_LOGOUT or there * was some nasty error @@ -1542,7 +1071,7 @@ static void iscsi_logout_timedout(void *data) static int iscsi_send_logout(iscsi_conn_t *conn) { struct iscsi_logout hdr; - struct iscsi_conn_context *conn_context; + struct iscsi_ev_context *ev_context; if (conn->state != STATE_LOGGED_IN) return EINVAL; @@ -1558,12 +1087,12 @@ static int iscsi_send_logout(iscsi_conn_t *conn) return EIO; conn->state = STATE_IN_LOGOUT; - conn_context = iscsi_conn_context_get(conn, 0); - if (!conn_context) + ev_context = iscsi_ev_context_get(conn, 0); + if (!ev_context) /* unbounded logout */ log_warning("Could not allocate conn context for logout."); else { - iscsi_sched_conn_context(conn_context, conn, + iscsi_sched_ev_context(ev_context, conn, conn->logout_timeout, EV_CONN_LOGOUT_TIMER); log_debug(3, "logout timeout timer %u\n", @@ -1575,11 +1104,11 @@ static int iscsi_send_logout(iscsi_conn_t *conn) static void iscsi_stop(void *data) { - struct iscsi_conn_context *conn_context = data; - struct iscsi_conn *conn = conn_context->conn; + struct iscsi_ev_context *ev_context = data; + struct iscsi_conn *conn = ev_context->conn; int rc = 0; - iscsi_conn_context_put(conn_context); + iscsi_ev_context_put(ev_context); if (!iscsi_send_logout(conn)) return; @@ -1741,11 +1270,11 @@ failed: static void session_conn_recv_pdu(void *data) { - struct iscsi_conn_context *conn_context = data; - iscsi_conn_t *conn = conn_context->conn; + struct iscsi_ev_context *ev_context = data; + iscsi_conn_t *conn = ev_context->conn; struct iscsi_hdr hdr; - conn->recv_context = conn_context; + conn->recv_context = ev_context; switch (conn->state) { case STATE_IN_LOGIN: @@ -1755,11 +1284,10 @@ static void session_conn_recv_pdu(void *data) case STATE_IN_LOGOUT: case STATE_LOGOUT_REQUESTED: /* read incoming PDU */ - if (!iscsi_io_recv_pdu(conn, &hdr, ISCSI_DIGEST_NONE, - conn->data, ISCSI_DEF_MAX_RECV_SEG_LEN, - ISCSI_DIGEST_NONE, 0)) { + if (iscsi_io_recv_pdu(conn, &hdr, ISCSI_DIGEST_NONE, + conn->data, ISCSI_DEF_MAX_RECV_SEG_LEN, + ISCSI_DIGEST_NONE, 0) < 0) return; - } switch (hdr.opcode & ISCSI_OPCODE_MASK) { case ISCSI_OP_NOOP_IN: @@ -1777,17 +1305,17 @@ static void session_conn_recv_pdu(void *data) } break; case STATE_XPT_WAIT: - iscsi_conn_context_put(conn_context); + iscsi_ev_context_put(ev_context); log_debug(1, "ignoring incoming PDU in XPT_WAIT. " "let connection re-establish or fail"); break; case STATE_CLEANUP_WAIT: - iscsi_conn_context_put(conn_context); + iscsi_ev_context_put(ev_context); log_debug(1, "ignoring incoming PDU in XPT_WAIT. " "let connection cleanup"); break; default: - iscsi_conn_context_put(conn_context); + iscsi_ev_context_put(ev_context); log_error("Invalid state. Dropping PDU.\n"); } } @@ -1910,15 +1438,15 @@ retry_create: static void session_conn_poll(void *data) { - struct iscsi_conn_context *conn_context = data; - iscsi_conn_t *conn = conn_context->conn; + struct iscsi_ev_context *ev_context = data; + iscsi_conn_t *conn = ev_context->conn; struct iscsi_session *session = conn->session; mgmt_ipc_err_e err = MGMT_IPC_OK; - queue_task_t *qtask = conn_context->data; + queue_task_t *qtask = ev_context->data; iscsi_login_context_t *c = &conn->login_context; int rc; - iscsi_conn_context_put(conn_context); + iscsi_ev_context_put(ev_context); if (conn->state != STATE_XPT_WAIT) return; @@ -1927,16 +1455,16 @@ static void session_conn_poll(void *data) if (rc == 0) { log_debug(4, "poll not connected %d", rc); /* timedout: Poll again. */ - conn_context = iscsi_conn_context_get(conn, 0); - if (!conn_context) { + ev_context = iscsi_ev_context_get(conn, 0); + if (!ev_context) { /* while polling the recv pool should be full */ log_error("BUG: session_conn_poll could not get conn " "context."); iscsi_login_eh(conn, qtask, MGMT_IPC_ERR_INTERNAL); return; } - conn_context->data = qtask; - iscsi_sched_conn_context(conn_context, conn, 0, EV_CONN_POLL); + ev_context->data = qtask; + iscsi_sched_ev_context(ev_context, conn, 0, EV_CONN_POLL); } else if (rc > 0) { /* connected! */ memset(c, 0, sizeof(iscsi_login_context_t)); @@ -1961,10 +1489,9 @@ static void session_conn_poll(void *data) "%d:%d", session->id, conn->id); } - iscsi_copy_operational_params(conn); - - if (session->t->template->create_conn) - session->t->template->create_conn(conn); + iscsi_copy_operational_params(conn, + &session->nrec.session.iscsi, + &session->nrec.conn[conn->id].iscsi); /* * TODO: use the iface number or some other value * so this will be persistent @@ -2011,70 +1538,55 @@ cleanup: session_conn_shutdown(conn, qtask, err); } -void iscsi_sched_conn_context(struct iscsi_conn_context *conn_context, - struct iscsi_conn *conn, unsigned long tmo, - int event) +static int iscsi_sched_ev_context(struct iscsi_ev_context *ev_context, + struct iscsi_conn *conn, unsigned long tmo, + int event) { enum iscsi_err error; log_debug(7, "sched conn context %p event %d, tmo %lu", - &conn_context->actor, event, tmo); + &ev_context->actor, event, tmo); - conn_context->conn = conn; + ev_context->conn = conn; switch (event) { case EV_CONN_RECV_PDU: - actor_new(&conn_context->actor, session_conn_recv_pdu, - conn_context); - actor_schedule(&conn_context->actor); + actor_new(&ev_context->actor, session_conn_recv_pdu, + ev_context); + actor_schedule(&ev_context->actor); break; case EV_CONN_ERROR: - error = *(enum iscsi_err *)conn_context->data; + error = *(enum iscsi_err *)ev_context->data; - actor_new(&conn_context->actor, session_conn_error, - conn_context); + actor_new(&ev_context->actor, session_conn_error, + ev_context); /* * We handle invalid host, by killing the session. * It must go at the head of the queue, so we do not * initiate error handling or logout or some other op. */ if (error == ISCSI_ERR_INVALID_HOST) - actor_schedule_head(&conn_context->actor); + actor_schedule_head(&ev_context->actor); else - actor_schedule(&conn_context->actor); + actor_schedule(&ev_context->actor); break; case EV_CONN_POLL: - actor_new(&conn_context->actor, session_conn_poll, - conn_context); - actor_schedule(&conn_context->actor); + actor_new(&ev_context->actor, session_conn_poll, + ev_context); + actor_schedule(&ev_context->actor); break; case EV_CONN_LOGOUT_TIMER: - actor_timer(&conn_context->actor, tmo * 1000, - iscsi_logout_timedout, conn_context); + actor_timer(&ev_context->actor, tmo * 1000, + iscsi_logout_timedout, ev_context); break; case EV_CONN_STOP: - actor_new(&conn_context->actor, iscsi_stop, - conn_context); - actor_schedule(&conn_context->actor); + actor_new(&ev_context->actor, iscsi_stop, + ev_context); + actor_schedule(&ev_context->actor); break; default: log_error("Invalid event type %d.", event); - return; - } -} - -iscsi_session_t* -session_find_by_sid(int sid) -{ - struct iscsi_transport *t; - iscsi_session_t *session; - - list_for_each_entry(t, &transports, list) { - list_for_each_entry(session, &t->sessions, list) { - if (session->id == sid) - return session; - } } - return NULL; + return 0; } static iscsi_session_t* session_find_by_rec(node_rec_t *rec) @@ -2111,50 +1623,6 @@ static int session_is_running(node_rec_t *rec) return 0; } -static int iface_set_param(struct iscsi_transport *t, struct iface_rec *iface, - struct iscsi_session *session) -{ - int rc = 0; - - log_debug(3, "setting iface %s, dev %s, set ip %s, hw %s, " - "transport %s.\n", - iface->name, iface->netdev, iface->ipaddress, - iface->hwaddress, iface->transport_name); - - if (!t->template->set_host_ip) - return 0; - - /* if we need to set the ip addr then set all the iface net settings */ - if (!iface_is_bound_by_ipaddr(iface)) { - log_warning("Please set the iface.ipaddress for iface %s, " - "then retry the login command.\n", iface->name); - return EINVAL; - } - - rc = __iscsi_host_set_param(t, session->hostno, - ISCSI_HOST_PARAM_IPADDRESS, - iface->ipaddress, ISCSI_STRING); - if (rc) - return rc; - - if (iface_is_bound_by_netdev(iface)) { - rc = __iscsi_host_set_param(t, session->hostno, - ISCSI_HOST_PARAM_NETDEV_NAME, - iface->netdev, ISCSI_STRING); - if (rc) - return rc; - } - - if (iface_is_bound_by_hwaddr(iface)) { - rc = __iscsi_host_set_param(t, session->hostno, - ISCSI_HOST_PARAM_HWADDRESS, - iface->hwaddress, ISCSI_STRING); - if (rc) - return rc; - } - return 0; -} - int session_login_task(node_rec_t *rec, queue_task_t *qtask) { @@ -2168,8 +1636,6 @@ session_login_task(node_rec_t *rec, queue_task_t *qtask) t = iscsi_sysfs_get_transport_by_name(rec->iface.transport_name); if (!t) return MGMT_IPC_ERR_TRANS_NOT_FOUND; - if (set_transport_template(t)) - return MGMT_IPC_ERR_TRANS_NOT_FOUND; if ((!(t->caps & CAP_RECOVERY_L0) && rec->session.iscsi.ERL != 0) || @@ -2234,7 +1700,7 @@ session_login_task(node_rec_t *rec, queue_task_t *qtask) conn = &session->conn[0]; qtask->conn = conn; - if (iface_set_param(t, &rec->iface, session)) { + if (iscsi_host_set_net_params(&rec->iface, session)) { __session_destroy(session); return MGMT_IPC_ERR_LOGIN_FAILURE; } @@ -2279,8 +1745,6 @@ iscsi_sync_session(node_rec_t *rec, queue_task_t *qtask, uint32_t sid) t = iscsi_sysfs_get_transport_by_name(rec->iface.transport_name); if (!t) return MGMT_IPC_ERR_TRANS_NOT_FOUND; - if (set_transport_template(t)) - return MGMT_IPC_ERR_TRANS_NOT_FOUND; session = __session_create(rec, t); if (!session) @@ -2392,7 +1856,7 @@ iscsi_host_send_targets(queue_task_t *qtask, int host_no, int do_login, struct iscsi_transport *t; t = iscsi_sysfs_get_transport_by_hba(host_no); - if (!t || set_transport_template(t)) { + if (!t) { log_error("Invalid host no %d for sendtargets\n", host_no); return MGMT_IPC_ERR_TRANS_FAILURE; } @@ -2412,7 +1876,7 @@ iscsi_host_send_targets(queue_task_t *qtask, int host_no, int do_login, * the card will have sessions preset in the FLASH and will log into them * automaotically then send us notification that a session is setup. */ -void iscsi_async_session_creation(uint32_t host_no, uint32_t sid) +static void iscsi_async_session_creation(uint32_t host_no, uint32_t sid) { struct iscsi_transport *transport; @@ -2428,7 +1892,20 @@ void iscsi_async_session_creation(uint32_t host_no, uint32_t sid) session_scan_host(NULL, host_no, NULL); } -void iscsi_async_session_destruction(uint32_t host_no, uint32_t sid) +static void iscsi_async_session_destruction(uint32_t host_no, uint32_t sid) { log_debug(3, "session destroyed sid %u host no %d", sid, host_no); } + +static struct iscsi_ipc_ev_clbk ipc_clbk = { + .create_session = iscsi_async_session_creation, + .destroy_session = iscsi_async_session_destruction, + .get_ev_context = iscsi_ev_context_get, + .put_ev_context = iscsi_ev_context_put, + .sched_ev_context = iscsi_sched_ev_context, +}; + +void iscsi_initiator_init(void) +{ + ipc_register_ev_callback(&ipc_clbk); +} diff --git a/usr/initiator.h b/usr/initiator.h index 2cf3f11..de73f71 100644 --- a/usr/initiator.h +++ b/usr/initiator.h @@ -112,14 +112,14 @@ typedef struct iscsi_login_context { struct iscsi_session; struct iscsi_conn; -struct iscsi_conn_context; +struct iscsi_ev_context; /* daemon's connection structure */ typedef struct iscsi_conn { uint32_t id; struct iscsi_session *session; iscsi_login_context_t login_context; - struct iscsi_conn_context *recv_context; + struct iscsi_ev_context *recv_context; struct queue_task *logout_qtask; char data[ISCSI_DEF_MAX_RECV_SEG_LEN]; char host[NI_MAXHOST]; /* scratch */ @@ -131,7 +131,7 @@ typedef struct iscsi_conn { actor_t nop_out_timer; #define CONTEXT_POOL_MAX 32 - struct iscsi_conn_context *context_pool[CONTEXT_POOL_MAX]; + struct iscsi_ev_context *context_pool[CONTEXT_POOL_MAX]; /* login state machine */ int current_stage; @@ -140,6 +140,11 @@ typedef struct iscsi_conn { conn_login_status_e status; /* tcp/socket settings */ + + /* + * Either a tcp/ip or a netlink socket to do + * IO through. + */ int socket_fd; /* address being used for normal session connection */ struct sockaddr_storage saddr; @@ -173,7 +178,7 @@ typedef struct iscsi_conn { uint32_t max_xmit_dlength; /* the value declared by the target */ } iscsi_conn_t; -struct iscsi_conn_context { +struct iscsi_ev_context { struct actor actor; struct iscsi_conn *conn; int allocated; @@ -201,6 +206,7 @@ typedef struct iscsi_session { uint32_t hostno; char netdev[IFNAMSIZ]; struct iscsi_transport *t; + uint8_t use_ipc; node_rec_t nrec; /* copy of original Node record in database */ unsigned int irrelevant_keys_bitmap; int send_async_text; @@ -242,7 +248,6 @@ typedef struct iscsi_session { uint8_t password_in[AUTH_STR_MAX_LEN]; int password_in_length; iscsi_conn_t conn[ISCSI_CONN_MAX]; - int ctrl_fd; uint64_t param_mask; /* connection reopens during recovery */ @@ -330,20 +335,25 @@ extern int iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, /* initiator.c */ extern int session_login_task(node_rec_t *rec, queue_task_t *qtask); extern int session_logout_task(int sid, queue_task_t *qtask); -extern iscsi_session_t *session_find_by_sid(int sid); -extern struct iscsi_conn_context *iscsi_conn_context_get(iscsi_conn_t *conn, - int ev_size); -extern void iscsi_conn_context_put(struct iscsi_conn_context *conn_context); -extern void iscsi_sched_conn_context(struct iscsi_conn_context *context, - struct iscsi_conn *conn, unsigned long tmo, - int event); +extern iscsi_session_t *session_find_by_sid(uint32_t sid); extern mgmt_ipc_err_e iscsi_sync_session(node_rec_t *rec, queue_task_t *tsk, uint32_t sid); extern mgmt_ipc_err_e iscsi_host_send_targets(queue_task_t *qtask, int host_no, int do_login, struct sockaddr_storage *ss); -extern mgmt_ipc_err_e iscsi_host_set_param(int host_no, int param, char *value); -extern void iscsi_async_session_creation(uint32_t host_no, uint32_t sid); -extern void iscsi_async_session_destruction(uint32_t host_no, uint32_t sid); + extern void free_initiator(void); +extern void iscsi_initiator_init(void); + +/* initiator code common to discovery and normal sessions */ +extern int iscsi_session_set_params(struct iscsi_conn *conn); +extern int iscsi_host_set_params(struct iscsi_session *session); +extern int iscsi_host_set_net_params(struct iface_rec *iface, + struct iscsi_session *session); +extern void iscsi_copy_operational_params(struct iscsi_conn *conn, + struct iscsi_session_operational_config *session_conf, + struct iscsi_conn_operational_config *conn_conf); +extern int iscsi_setup_authentication(struct iscsi_session *session, + struct iscsi_auth_config *auth_cfg); +extern int iscsi_setup_portal(struct iscsi_conn *conn, char *address, int port); #endif /* INITIATOR_H */ diff --git a/usr/initiator_common.c b/usr/initiator_common.c new file mode 100644 index 0000000..8a61da3 --- /dev/null +++ b/usr/initiator_common.c @@ -0,0 +1,600 @@ +/* + * Common code for setting up discovery and normal sessions. + * + * Copyright (C) 2004 Dmitry Yusupov, Alex Aizman + * Copyright (C) 2006 - 2009 Mike Christie + * Copyright (C) 2006 - 2009 Red Hat, Inc. All rights reserved. + * maintained by open-iscsi@googlegroups.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * See the file COPYING included with this distribution for more details. + */ + +#include +#include +#include +#include + +#include "initiator.h" +#include "transport.h" +#include "iscsid.h" +#include "iscsi_ipc.h" +#include "log.h" +#include "iscsi_sysfs.h" +#include "iscsi_settings.h" +#include "iface.h" +#include "host.h" +#include "sysdeps.h" + +struct iscsi_session *session_find_by_sid(uint32_t sid) +{ + struct iscsi_transport *t; + struct iscsi_session *session; + + list_for_each_entry(t, &transports, list) { + list_for_each_entry(session, &t->sessions, list) { + if (session->id == sid) + return session; + } + } + return NULL; +} + +/* + * calculate parameter's padding + */ +static unsigned int +__padding(unsigned int param) +{ + int pad; + + pad = param & 3; + if (pad) { + pad = 4 - pad; + log_debug(1, "parameter's value %d padded to %d bytes\n", + param, param + pad); + } + return param + pad; +} + +int iscsi_setup_authentication(struct iscsi_session *session, + struct iscsi_auth_config *auth_cfg) +{ + /* if we have any incoming credentials, we insist on authenticating + * the target or not logging in at all + */ + if (auth_cfg->username_in[0] || auth_cfg->password_in_length) { + /* sanity check the config */ + if (auth_cfg->password_length == 0) { + log_warning("CHAP configuratoin has incoming " + "authentication credentials but has no " + "outgoing credentials configured."); + return EINVAL; + } + session->bidirectional_auth = 1; + } else { + /* no or 1-way authentication */ + session->bidirectional_auth = 0; + } + + /* copy in whatever credentials we have */ + strlcpy(session->username, auth_cfg->username, + sizeof (session->username)); + session->username[sizeof (session->username) - 1] = '\0'; + if ((session->password_length = auth_cfg->password_length)) + memcpy(session->password, auth_cfg->password, + session->password_length); + + strlcpy(session->username_in, auth_cfg->username_in, + sizeof (session->username_in)); + session->username_in[sizeof (session->username_in) - 1] = '\0'; + if ((session->password_in_length = + auth_cfg->password_in_length)) + memcpy(session->password_in, auth_cfg->password_in, + session->password_in_length); + + if (session->password_length || session->password_in_length) { + /* setup the auth buffers */ + session->auth_buffers[0].address = &session->auth_client_block; + session->auth_buffers[0].length = + sizeof (session->auth_client_block); + session->auth_buffers[1].address = + &session->auth_recv_string_block; + session->auth_buffers[1].length = + sizeof (session->auth_recv_string_block); + + session->auth_buffers[2].address = + &session->auth_send_string_block; + session->auth_buffers[2].length = + sizeof (session->auth_send_string_block); + + session->auth_buffers[3].address = + &session->auth_recv_binary_block; + session->auth_buffers[3].length = + sizeof (session->auth_recv_binary_block); + + session->auth_buffers[4].address = + &session->auth_send_binary_block; + session->auth_buffers[4].length = + sizeof (session->auth_send_binary_block); + + session->num_auth_buffers = 5; + log_debug(6, "authentication setup complete..."); + } else { + session->num_auth_buffers = 0; + log_debug(6, "no authentication configured..."); + } + + return 0; +} + +void +iscsi_copy_operational_params(struct iscsi_conn *conn, + struct iscsi_session_operational_config *session_conf, + struct iscsi_conn_operational_config *conn_conf) +{ + struct iscsi_session *session = conn->session; + struct iscsi_transport *t = session->t; + + conn->hdrdgst_en = conn_conf->HeaderDigest; + conn->datadgst_en = conn_conf->DataDigest; + + conn->max_recv_dlength = + __padding(conn_conf->MaxRecvDataSegmentLength); + if (conn->max_recv_dlength < ISCSI_MIN_MAX_RECV_SEG_LEN || + conn->max_recv_dlength > ISCSI_MAX_MAX_RECV_SEG_LEN) { + log_error("Invalid iscsi.MaxRecvDataSegmentLength. Must be " + "within %u and %u. Setting to %u\n", + ISCSI_MIN_MAX_RECV_SEG_LEN, + ISCSI_MAX_MAX_RECV_SEG_LEN, + DEF_INI_MAX_RECV_SEG_LEN); + conn_conf->MaxRecvDataSegmentLength = + DEF_INI_MAX_RECV_SEG_LEN; + conn->max_recv_dlength = DEF_INI_MAX_RECV_SEG_LEN; + } + + /* zero indicates to use the target's value */ + conn->max_xmit_dlength = + __padding(conn_conf->MaxXmitDataSegmentLength); + if (conn->max_xmit_dlength == 0) + conn->max_xmit_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN; + if (conn->max_xmit_dlength < ISCSI_MIN_MAX_RECV_SEG_LEN || + conn->max_xmit_dlength > ISCSI_MAX_MAX_RECV_SEG_LEN) { + log_error("Invalid iscsi.MaxXmitDataSegmentLength. Must be " + "within %u and %u. Setting to %u\n", + ISCSI_MIN_MAX_RECV_SEG_LEN, + ISCSI_MAX_MAX_RECV_SEG_LEN, + DEF_INI_MAX_RECV_SEG_LEN); + conn_conf->MaxXmitDataSegmentLength = + DEF_INI_MAX_RECV_SEG_LEN; + conn->max_xmit_dlength = DEF_INI_MAX_RECV_SEG_LEN; + } + + /* session's operational parameters */ + session->initial_r2t_en = session_conf->InitialR2T; + session->imm_data_en = session_conf->ImmediateData; + session->first_burst = __padding(session_conf->FirstBurstLength); + /* + * some targets like netapp fail the login if sent bad first_burst + * and max_burst lens, even when immediate data=no and + * initial r2t = Yes, so we always check the user values. + */ + if (session->first_burst < ISCSI_MIN_FIRST_BURST_LEN || + session->first_burst > ISCSI_MAX_FIRST_BURST_LEN) { + log_error("Invalid iscsi.FirstBurstLength of %u. Must be " + "within %u and %u. Setting to %u\n", + session->first_burst, + ISCSI_MIN_FIRST_BURST_LEN, + ISCSI_MAX_FIRST_BURST_LEN, + DEF_INI_FIRST_BURST_LEN); + session_conf->FirstBurstLength = DEF_INI_FIRST_BURST_LEN; + session->first_burst = DEF_INI_FIRST_BURST_LEN; + } + + session->max_burst = __padding(session_conf->MaxBurstLength); + if (session->max_burst < ISCSI_MIN_MAX_BURST_LEN || + session->max_burst > ISCSI_MAX_MAX_BURST_LEN) { + log_error("Invalid iscsi.MaxBurstLength of %u. Must be " + "within %u and %u. Setting to %u\n", + session->max_burst, ISCSI_MIN_MAX_BURST_LEN, + ISCSI_MAX_MAX_BURST_LEN, DEF_INI_MAX_BURST_LEN); + session_conf->MaxBurstLength = DEF_INI_MAX_BURST_LEN; + session->max_burst = DEF_INI_MAX_BURST_LEN; + } + + if (session->first_burst > session->max_burst) { + log_error("Invalid iscsi.FirstBurstLength of %u. Must be " + "less than iscsi.MaxBurstLength. Setting to %u\n", + session->first_burst, session->max_burst); + session_conf->FirstBurstLength = session->max_burst; + session->first_burst = session->max_burst; + } + + session->def_time2wait = session_conf->DefaultTime2Wait; + session->def_time2retain = session_conf->DefaultTime2Retain; + session->erl = session_conf->ERL; + + if (session->type == ISCSI_SESSION_TYPE_DISCOVERY) { + /* + * Right now, we only support 8K max for kernel based + * sendtargets discovery, because the recv pdu buffers are + * limited to this size. + */ + if ((t->caps & CAP_TEXT_NEGO) && + conn->max_recv_dlength > ISCSI_DEF_MAX_RECV_SEG_LEN) + conn->max_recv_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN; + + /* We do not support discovery sessions with digests */ + conn->hdrdgst_en = ISCSI_DIGEST_NONE; + conn->datadgst_en = ISCSI_DIGEST_NONE; + } + + if (t->template->create_conn) + t->template->create_conn(conn); +} + +int iscsi_setup_portal(struct iscsi_conn *conn, char *address, int port) +{ + char serv[NI_MAXSERV]; + + sprintf(serv, "%d", port); + if (resolve_address(address, serv, &conn->saddr)) { + log_error("cannot resolve host name %s", address); + return EINVAL; + } + conn->failback_saddr = conn->saddr; + + getnameinfo((struct sockaddr *)&conn->saddr, sizeof(conn->saddr), + conn->host, sizeof(conn->host), NULL, 0, NI_NUMERICHOST); + log_debug(4, "resolved %s to %s", address, conn->host); + return 0; +} + +int host_set_param(struct iscsi_transport *t, + uint32_t host_no, int param, char *value, + int type) +{ + int rc; + + rc = ipc->set_host_param(t->handle, host_no, param, value, type); + /* 2.6.20 and below returns EINVAL */ + if (rc && rc != -ENOSYS && rc != -EINVAL) { + log_error("can't set operational parameter %d for " + "host %d, retcode %d (%d)", param, host_no, + rc, errno); + return rc; + } + return 0; +} + +static void print_param_value(enum iscsi_param param, void *value, int type) +{ + log_debug(3, "set operational parameter %d to:", param); + + if (type == ISCSI_STRING) + log_debug(3, "%s", value ? (char *)value : "NULL"); + else + log_debug(3, "%u", *(uint32_t *)value); +} + +#define MAX_HOST_PARAMS 2 + +int iscsi_host_set_params(struct iscsi_session *session) +{ + struct iscsi_transport *t = session->t; + int i; + struct hostparam { + int param; + int type; + void *value; + } hosttbl[MAX_HOST_PARAMS] = { + { + .param = ISCSI_HOST_PARAM_NETDEV_NAME, + .value = session->nrec.iface.netdev, + .type = ISCSI_STRING, + }, { + .param = ISCSI_HOST_PARAM_HWADDRESS, + .value = session->nrec.iface.hwaddress, + .type = ISCSI_STRING, + }, + }; + + for (i = 0; i < MAX_HOST_PARAMS; i++) { + if (host_set_param(t, session->hostno, + hosttbl[i].param, hosttbl[i].value, + hosttbl[i].type)) { + return EPERM; + } + + print_param_value(hosttbl[i].param, hosttbl[i].value, + hosttbl[i].type); + } + + return 0; +} + +#define MAX_SESSION_PARAMS 32 + +int iscsi_session_set_params(struct iscsi_conn *conn) +{ + struct iscsi_session *session = conn->session; + struct iscsi_transport *t = session->t; + int i, rc; + uint32_t one = 1, zero = 0; + struct connparam { + int param; + int type; + void *value; + int conn_only; + } conntbl[MAX_SESSION_PARAMS] = { + { + .param = ISCSI_PARAM_MAX_RECV_DLENGTH, + .value = &conn->max_recv_dlength, + .type = ISCSI_INT, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_MAX_XMIT_DLENGTH, + .value = &conn->max_xmit_dlength, + .type = ISCSI_INT, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_HDRDGST_EN, + .value = &conn->hdrdgst_en, + .type = ISCSI_INT, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_DATADGST_EN, + .value = &conn->datadgst_en, + .type = ISCSI_INT, + .conn_only = 1, + }, { + .param = ISCSI_PARAM_INITIAL_R2T_EN, + .value = &session->initial_r2t_en, + .type = ISCSI_INT, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_MAX_R2T, + .value = &one, /* FIXME: session->max_r2t */ + .type = ISCSI_INT, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_IMM_DATA_EN, + .value = &session->imm_data_en, + .type = ISCSI_INT, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_FIRST_BURST, + .value = &session->first_burst, + .type = ISCSI_INT, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_MAX_BURST, + .value = &session->max_burst, + .type = ISCSI_INT, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_PDU_INORDER_EN, + .value = &session->pdu_inorder_en, + .type = ISCSI_INT, + .conn_only = 0, + }, { + .param =ISCSI_PARAM_DATASEQ_INORDER_EN, + .value = &session->dataseq_inorder_en, + .type = ISCSI_INT, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_ERL, + .value = &zero, /* FIXME: session->erl */ + .type = ISCSI_INT, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_IFMARKER_EN, + .value = &zero,/* FIXME: session->ifmarker_en */ + .type = ISCSI_INT, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_OFMARKER_EN, + .value = &zero,/* FIXME: session->ofmarker_en */ + .type = ISCSI_INT, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_EXP_STATSN, + .value = &conn->exp_statsn, + .type = ISCSI_INT, + .conn_only = 1, + }, { + .param = ISCSI_PARAM_TARGET_NAME, + .conn_only = 0, + .type = ISCSI_STRING, + .value = session->target_name, + }, { + .param = ISCSI_PARAM_TPGT, + .value = &session->portal_group_tag, + .type = ISCSI_INT, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_PERSISTENT_ADDRESS, + .value = session->nrec.conn[conn->id].address, + .type = ISCSI_STRING, + .conn_only = 1, + }, { + .param = ISCSI_PARAM_PERSISTENT_PORT, + .value = &session->nrec.conn[conn->id].port, + .type = ISCSI_INT, + .conn_only = 1, + }, { + .param = ISCSI_PARAM_SESS_RECOVERY_TMO, + .value = &session->replacement_timeout, + .type = ISCSI_INT, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_USERNAME, + .value = session->username, + .type = ISCSI_STRING, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_USERNAME_IN, + .value = session->username_in, + .type = ISCSI_STRING, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_PASSWORD, + .value = session->password, + .type = ISCSI_STRING, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_PASSWORD_IN, + .value = session->password_in, + .type = ISCSI_STRING, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_FAST_ABORT, + .value = &session->fast_abort, + .type = ISCSI_INT, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_ABORT_TMO, + .value = &session->abort_timeout, + .type = ISCSI_INT, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_LU_RESET_TMO, + .value = &session->lu_reset_timeout, + .type = ISCSI_INT, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_TGT_RESET_TMO, + .value = &session->tgt_reset_timeout, + .type = ISCSI_INT, + .conn_only = 0, + }, { + .param = ISCSI_PARAM_PING_TMO, + .value = &conn->noop_out_timeout, + .type = ISCSI_INT, + .conn_only = 1, + }, { + .param = ISCSI_PARAM_RECV_TMO, + .value = &conn->noop_out_interval, + .type = ISCSI_INT, + .conn_only = 1, + }, { + .param = ISCSI_PARAM_IFACE_NAME, + .value = session->nrec.iface.name, + .type = ISCSI_STRING, + }, { + .param = ISCSI_PARAM_INITIATOR_NAME, + .value = session->initiator_name, + .type = ISCSI_STRING, + }, + }; + + session->param_mask = ~0ULL; + if (!(t->caps & CAP_MULTI_R2T)) + session->param_mask &= ~ISCSI_MAX_R2T; + if (!(t->caps & CAP_HDRDGST)) + session->param_mask &= ~ISCSI_HDRDGST_EN; + if (!(t->caps & CAP_DATADGST)) + session->param_mask &= ~ISCSI_DATADGST_EN; + if (!(t->caps & CAP_MARKERS)) { + session->param_mask &= ~ISCSI_IFMARKER_EN; + session->param_mask &= ~ISCSI_OFMARKER_EN; + } + + /* Entered full-feature phase! */ + for (i = 0; i < MAX_SESSION_PARAMS; i++) { + if (conn->id != 0 && !conntbl[i].conn_only) + continue; + + if (!(session->param_mask & (1ULL << conntbl[i].param))) + continue; + + rc = ipc->set_param(session->t->handle, session->id, + conn->id, conntbl[i].param, conntbl[i].value, + conntbl[i].type); + if (rc && rc != -ENOSYS) { + log_error("can't set operational parameter %d for " + "connection %d:%d, retcode %d (%d)", + conntbl[i].param, session->id, conn->id, + rc, errno); + return EPERM; + } + + if (rc == -ENOSYS) { + switch (conntbl[i].param) { + case ISCSI_PARAM_PING_TMO: + /* + * older kernels may not support nops + * in kernel + */ + conn->userspace_nop = 1; + break; +#if 0 +TODO handle this + case ISCSI_PARAM_INITIATOR_NAME: + /* use host level one instead */ + hosttbl[ISCSI_HOST_PARAM_INITIATOR_NAME].set = 1; + break; +#endif + } + } + + print_param_value(conntbl[i].param, conntbl[i].value, + conntbl[i].type); + } + + return 0; +} + +int iscsi_host_set_net_params(struct iface_rec *iface, + struct iscsi_session *session) +{ + struct iscsi_transport *t = session->t; + int rc = 0; + + log_debug(3, "setting iface %s, dev %s, set ip %s, hw %s, " + "transport %s.\n", + iface->name, iface->netdev, iface->ipaddress, + iface->hwaddress, iface->transport_name); + + if (!t->template->set_host_ip) + return 0; + + /* if we need to set the ip addr then set all the iface net settings */ + if (!iface_is_bound_by_ipaddr(iface)) { + log_warning("Please set the iface.ipaddress for iface %s, " + "then retry the login command.\n", iface->name); + return EINVAL; + } + + rc = host_set_param(t, session->hostno, + ISCSI_HOST_PARAM_IPADDRESS, + iface->ipaddress, ISCSI_STRING); + if (rc) + return rc; + + if (iface_is_bound_by_netdev(iface)) { + rc = host_set_param(t, session->hostno, + ISCSI_HOST_PARAM_NETDEV_NAME, + iface->netdev, ISCSI_STRING); + if (rc) + return rc; + } + + if (iface_is_bound_by_hwaddr(iface)) { + rc = host_set_param(t, session->hostno, + ISCSI_HOST_PARAM_HWADDRESS, + iface->hwaddress, ISCSI_STRING); + if (rc) + return rc; + } + return 0; +} diff --git a/usr/io.c b/usr/io.c index 8fb806d..24a09d6 100644 --- a/usr/io.c +++ b/usr/io.c @@ -503,7 +503,7 @@ iscsi_io_send_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, /* set a timeout, since the socket calls may take a long time * to timeout on their own */ - if (!ipc) { + if (!session->use_ipc) { memset(&action, 0, sizeof (struct sigaction)); memset(&old, 0, sizeof (struct sigaction)); action.sa_sigaction = NULL; @@ -566,7 +566,7 @@ iscsi_io_send_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, else pad_bytes = 0; - if (ipc) + if (session->use_ipc) ipc->send_pdu_begin(session->t->handle, session->id, conn->id, end - header, ntoh24(hdr->dlength) + pad_bytes); @@ -575,8 +575,8 @@ iscsi_io_send_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, vec[0].iov_base = header; vec[0].iov_len = end - header; - if (!ipc) - rc = writev(session->ctrl_fd, vec, 1); + if (!session->use_ipc) + rc = writev(conn->socket_fd, vec, 1); else rc = ipc->writev(0, vec, 1); if (timedout) { @@ -603,13 +603,13 @@ iscsi_io_send_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, vec[1].iov_base = (void *) &pad; vec[1].iov_len = pad_bytes; - if (!ipc) - rc = writev(session->ctrl_fd, vec, 2); + if (!session->use_ipc) + rc = writev(conn->socket_fd, vec, 2); else rc = ipc->writev(0, vec, 2); if (timedout) { log_error("socket %d write timed out", - conn->socket_fd); + conn->socket_fd); ret = 0; goto done; } else if ((rc <= 0) && (errno != EAGAIN)) { @@ -627,7 +627,7 @@ iscsi_io_send_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, } } - if (ipc) { + if (session->use_ipc) { if (ipc->send_pdu_end(session->t->handle, session->id, conn->id, &rc)) { ret = 0; @@ -638,7 +638,7 @@ iscsi_io_send_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, ret = 1; done: - if (!ipc) { + if (!session->use_ipc) { alarm(0); sigaction(SIGALRM, &old, NULL); timedout = 0; @@ -670,7 +670,7 @@ iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, /* set a timeout, since the socket calls may take a long * time to timeout on their own */ - if (!ipc) { + if (!session->use_ipc) { memset(&action, 0, sizeof (struct sigaction)); memset(&old, 0, sizeof (struct sigaction)); action.sa_sigaction = NULL; @@ -680,7 +680,10 @@ iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, timedout = 0; alarm(timeout); } else { - if (ipc->recv_pdu_begin(conn)) { + failed = ipc->recv_pdu_begin(conn); + if (failed == -EAGAIN) + return -EAGAIN; + else if (failed < 0) { failed = 1; goto done; } @@ -688,14 +691,14 @@ iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, /* read a response header */ do { - if (!ipc) - rlen = read(session->ctrl_fd, header, + if (!session->use_ipc) + rlen = read(conn->socket_fd, header, sizeof (*hdr) - h_bytes); else rlen = ipc->read(header, sizeof (*hdr) - h_bytes); if (timedout) { log_error("socket %d header read timed out", - conn->socket_fd); + conn->socket_fd); failed = 1; goto done; } else if (rlen == 0) { @@ -714,7 +717,7 @@ iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, } while (h_bytes < sizeof (*hdr)); log_debug(4, "read %d PDU header bytes, opcode 0x%x, dlength %u, " - "data %p, max %u", h_bytes, hdr->opcode, + "data %p, max %u", h_bytes, hdr->opcode & ISCSI_OPCODE_MASK, ntoh24(hdr->dlength), data, max_data_length); /* check for additional headers */ @@ -745,14 +748,14 @@ iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, /* read the rest into our buffer */ d_bytes = 0; while (d_bytes < dlength) { - if (!ipc) - rlen = read(session->ctrl_fd, data + d_bytes, + if (!session->use_ipc) + rlen = read(conn->socket_fd, data + d_bytes, dlength - d_bytes); else rlen = ipc->read(data + d_bytes, dlength - d_bytes); if (timedout) { log_error("socket %d data read timed out", - conn->socket_fd); + conn->socket_fd); failed = 1; goto done; } else if (rlen == 0) { @@ -772,7 +775,7 @@ iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, /* handle PDU data padding. * data is padded in case of kernel_io */ pad = dlength % ISCSI_PAD_LEN; - if (pad && !ipc) { + if (pad && !session->use_ipc) { int pad_bytes = pad = ISCSI_PAD_LEN - pad; char bytes[ISCSI_PAD_LEN]; @@ -780,7 +783,7 @@ iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, rlen = read(conn->socket_fd, &bytes, pad_bytes); if (timedout) { log_error("socket %d pad read timed out", - conn->socket_fd); + conn->socket_fd); failed = 1; goto done; } else if (rlen == 0) { @@ -828,7 +831,7 @@ iscsi_io_recv_pdu(iscsi_conn_t *conn, struct iscsi_hdr *hdr, } done: - if (!ipc) { + if (!session->use_ipc) { alarm(0); sigaction(SIGALRM, &old, NULL); } else { @@ -840,7 +843,7 @@ done: if (timedout || failed) { timedout = 0; - return 0; + return -EIO; } return h_bytes + ahs_bytes + d_bytes; diff --git a/usr/iscsi_ipc.h b/usr/iscsi_ipc.h index 74ef948..93b4917 100644 --- a/usr/iscsi_ipc.h +++ b/usr/iscsi_ipc.h @@ -34,6 +34,26 @@ enum { }; struct iscsi_conn; +struct iscsi_ev_context; + +/* + * When handling async events, the initiator may not be able to + * handle the event in the same context, so this allows the interface + * code to call into the initiator to shedule handling. + */ +struct iscsi_ipc_ev_clbk { + void (*create_session) (uint32_t host_no, uint32_t sid); + void (*destroy_session) (uint32_t host_no, uint32_t sid); + + struct iscsi_ev_context *(*get_ev_context) (struct iscsi_conn *conn, + int ev_size); + void (*put_ev_context) (struct iscsi_ev_context *ev_context); + int (*sched_ev_context) (struct iscsi_ev_context *ev_context, + struct iscsi_conn *conn, + unsigned long tmo, int event); +}; + +extern void ipc_register_ev_callback(struct iscsi_ipc_ev_clbk *ipc_ev_clbk); /** * struct iscsi_ipc - Open-iSCSI Interface for Kernel IPC diff --git a/usr/iscsi_sysfs.c b/usr/iscsi_sysfs.c index 6eca3c8..a9c78c6 100644 --- a/usr/iscsi_sysfs.c +++ b/usr/iscsi_sysfs.c @@ -115,6 +115,10 @@ static int read_transports(void) INIT_LIST_HEAD(&t->list); strlcpy(t->name, namelist[i]->d_name, ISCSI_TRANSPORT_NAME_MAXLEN); + if (set_transport_template(t)) { + free(t); + return -1; + } } else log_debug(7, "Updating transport %s", namelist[i]->d_name); @@ -970,7 +974,8 @@ struct iscsi_transport *iscsi_sysfs_get_transport_by_name(char *transport_name) struct iscsi_transport *t; /* sync up kernel and userspace */ - read_transports(); + if (read_transports()) + return NULL; /* check if the transport is loaded and matches */ list_for_each_entry(t, &transports, list) { diff --git a/usr/iscsi_timer.c b/usr/iscsi_timer.c new file mode 100644 index 0000000..de38286 --- /dev/null +++ b/usr/iscsi_timer.c @@ -0,0 +1,86 @@ +/* + * iSCSI timer + * + * Copyright (C) 2002 Cisco Systems, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * See the file COPYING included with this distribution for more details. + */ +#include +#include + +void iscsi_timer_clear(struct timeval *timer) +{ + memset(timer, 0, sizeof (*timer)); +} + +/* set timer to now + seconds */ +void iscsi_timer_set(struct timeval *timer, int seconds) +{ + if (timer) { + memset(timer, 0, sizeof (*timer)); + gettimeofday(timer, NULL); + + timer->tv_sec += seconds; + } +} + +int iscsi_timer_expired(struct timeval *timer) +{ + struct timeval now; + + /* no timer, can't have expired */ + if ((timer == NULL) || ((timer->tv_sec == 0) && (timer->tv_usec == 0))) + return 0; + + memset(&now, 0, sizeof (now)); + gettimeofday(&now, NULL); + + if (now.tv_sec > timer->tv_sec) + return 1; + if ((now.tv_sec == timer->tv_sec) && (now.tv_usec >= timer->tv_usec)) + return 1; + return 0; +} + +int iscsi_timer_msecs_until(struct timeval *timer) +{ + struct timeval now; + int msecs; + long partial; + + /* no timer, can't have expired, infinite time til it expires */ + if ((timer == NULL) || ((timer->tv_sec == 0) && (timer->tv_usec == 0))) + return -1; + + memset(&now, 0, sizeof (now)); + gettimeofday(&now, NULL); + + /* already expired? */ + if (now.tv_sec > timer->tv_sec) + return 0; + if ((now.tv_sec == timer->tv_sec) && (now.tv_usec >= timer->tv_usec)) + return 0; + + /* not expired yet, do the math */ + partial = timer->tv_usec - now.tv_usec; + if (partial < 0) { + partial += 1000 * 1000; + msecs = (partial + 500) / 1000; + msecs += (timer->tv_sec - now.tv_sec - 1) * 1000; + } else { + msecs = (partial + 500) / 1000; + msecs += (timer->tv_sec - now.tv_sec) * 1000; + } + + return msecs; +} diff --git a/usr/iscsi_timer.h b/usr/iscsi_timer.h new file mode 100644 index 0000000..13e8368 --- /dev/null +++ b/usr/iscsi_timer.h @@ -0,0 +1,28 @@ +/* + * iSCSI timer + * + * Copyright (C) 2002 Cisco Systems, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * See the file COPYING included with this distribution for more details. + */ +#ifndef ISCSI_TIMER_H +#define ISCSI_TIMER_H + +struct timeval; + +extern void iscsi_timer_clear(struct timeval *timer); +extern void iscsi_timer_set(struct timeval *timer, int seconds); +extern int iscsi_timer_expired(struct timeval *timer); +extern int iscsi_timer_msecs_until(struct timeval *timer); + +#endif diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c index fcf2a3a..e9d0e7c 100644 --- a/usr/iscsiadm.c +++ b/usr/iscsiadm.c @@ -49,9 +49,9 @@ #include "iscsid_req.h" #include "isns-proto.h" -struct iscsi_ipc *ipc = NULL; /* dummy */ static char program_name[] = "iscsiadm"; static char config_file[TARGET_NAME_MAXLEN]; +extern struct iscsi_ipc *ipc; enum iscsiadm_mode { MODE_DISCOVERY, diff --git a/usr/iscsid.c b/usr/iscsid.c index a659176..bce80c8 100644 --- a/usr/iscsid.c +++ b/usr/iscsid.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include "iscsid.h" #include "mgmt_ipc.h" @@ -54,10 +56,10 @@ struct iscsi_daemon_config daemon_config; struct iscsi_daemon_config *dconfig = &daemon_config; static char program_name[] = "iscsid"; -int control_fd, mgmt_ipc_fd; static pid_t log_pid; static gid_t gid; static int daemonize = 1; +static int mgmt_ipc_fd; static struct option const long_options[] = { {"config", required_argument, NULL, 'c'}, @@ -196,11 +198,6 @@ static int sync_session(void *data, struct session_info *info) t = iscsi_sysfs_get_transport_by_sid(info->sid); if (!t) return 0; - if (set_transport_template(t)) { - log_error("Could not find userspace transport template for %s", - t->name); - return 0; - } /* * Just rescan the device in case this is the first startup. @@ -337,6 +334,7 @@ int main(int argc, char *argv[]) uid_t uid = 0; struct sigaction sa_old; struct sigaction sa_new; + int control_fd; pid_t pid; /* do not allow ctrl-c for now... */ @@ -498,6 +496,7 @@ int main(int argc, char *argv[]) } else reap_inc(); + iscsi_initiator_init(); increase_max_files(); discoveryd_start(daemon_config.initiator_name); diff --git a/usr/iscsid.h b/usr/iscsid.h index b646f32..15f264f 100644 --- a/usr/iscsid.h +++ b/usr/iscsid.h @@ -31,6 +31,5 @@ struct iscsi_daemon_config { char *initiator_alias; }; extern struct iscsi_daemon_config *dconfig; -extern int control_fd; #endif /* ISCSID_H */ diff --git a/usr/iscsistart.c b/usr/iscsistart.c index 8ae9a31..35d2a2f 100644 --- a/usr/iscsistart.c +++ b/usr/iscsistart.c @@ -57,10 +57,8 @@ static node_rec_t config_rec; static LIST_HEAD(targets); static char program_name[] = "iscsistart"; -static int mgmt_ipc_fd; /* used by initiator */ -int control_fd; extern struct iscsi_ipc *ipc; static struct option const long_options[] = { @@ -242,6 +240,7 @@ int main(int argc, char *argv[]) struct boot_context *context, boot_context; struct sigaction sa_old; struct sigaction sa_new; + int control_fd, mgmt_ipc_fd; pid_t pid; idbm_node_setup_defaults(&config_rec); @@ -420,6 +419,7 @@ int main(int argc, char *argv[]) /* * Start Main Event Loop */ + iscsi_initiator_init(); actor_init(); event_loop(ipc, control_fd, mgmt_ipc_fd); ipc->ctldev_close(); diff --git a/usr/login.c b/usr/login.c index be19b9e..db76c80 100644 --- a/usr/login.c +++ b/usr/login.c @@ -27,11 +27,14 @@ #include #include #include +#include +#include #include #include "initiator.h" #include "transport.h" #include "log.h" +#include "iscsi_timer.h" /* caller is assumed to be well-behaved and passing NUL terminated strings */ int @@ -1434,11 +1437,15 @@ int iscsi_login_rsp(iscsi_session_t *session, iscsi_login_context_t *c) { iscsi_conn_t *conn = &session->conn[c->cid]; + int err; /* read the target's response into the same buffer */ - if (!iscsi_io_recv_pdu(conn, &c->pdu, ISCSI_DIGEST_NONE, c->data, - c->max_data_length, ISCSI_DIGEST_NONE, - c->timeout)) { + err = iscsi_io_recv_pdu(conn, &c->pdu, ISCSI_DIGEST_NONE, c->data, + c->max_data_length, ISCSI_DIGEST_NONE, + c->timeout); + if (err == -EAGAIN) { + goto done; + } else if (err < 0) { /* * FIXME: caller might want us to distinguish I/O * error and timeout. Might want to switch portals on @@ -1449,6 +1456,7 @@ iscsi_login_rsp(iscsi_session_t *session, iscsi_login_context_t *c) goto done; } + err = -EIO; c->received_pdu = 1; /* check the PDU response type */ @@ -1490,7 +1498,7 @@ iscsi_login_rsp(iscsi_session_t *session, iscsi_login_context_t *c) if (c->ret == LOGIN_OK) c->ret = LOGIN_FAILED; } - return 1; + return err; } /** @@ -1514,7 +1522,9 @@ iscsi_login(iscsi_session_t *session, int cid, char *buffer, size_t bufsize, { iscsi_conn_t *conn = &session->conn[cid]; iscsi_login_context_t *c = &conn->login_context; - int ret; + struct timeval connection_timer; + struct pollfd pfd; + int ret, timeout; /* * assume iscsi_login is only called from discovery, so it is @@ -1532,15 +1542,63 @@ iscsi_login(iscsi_session_t *session, int cid, char *buffer, size_t bufsize, do { if (iscsi_login_req(session, c)) return c->ret; - ret = iscsi_login_rsp(session, c); - if (status_class) - *status_class = c->status_class; - if (status_detail) - *status_detail = c->status_detail; + /* + * TODO: merge the poll and req/rsp code with the discovery + * poll and text req/rsp. + */ + iscsi_timer_set(&connection_timer, + session->conn[0].active_timeout); + timeout = iscsi_timer_msecs_until(&connection_timer); + + memset(&pfd, 0, sizeof (pfd)); + pfd.fd = conn->socket_fd; + pfd.events = POLLIN | POLLPRI; + +repoll: + pfd.revents = 0; + ret = poll(&pfd, 1, timeout); + log_debug(7, "%s: Poll return %d\n", __FUNCTION__, ret); + if (iscsi_timer_expired(&connection_timer)) { + log_warning("Login response timeout. Waited %d " + "seconds and did not get reponse PDU.\n", + session->conn[0].active_timeout); + c->ret = LOGIN_FAILED; + return c->ret; + } + + if (ret > 0) { + if (pfd.revents & (POLLIN | POLLPRI)) { + ret = iscsi_login_rsp(session, c); + if (ret == -EAGAIN) + goto repoll; + + if (status_class) + *status_class = c->status_class; + if (status_detail) + *status_detail = c->status_detail; + + if (ret) + return c->ret; + } else if (pfd.revents & POLLHUP) { + log_warning("Login POLLHUP"); + c->ret = LOGIN_FAILED; + return c->ret; + } else if (pfd.revents & POLLNVAL) { + log_warning("Login POLLNVAL"); + c->ret = LOGIN_IO_ERROR; + return c->ret; + } else if (pfd.revents & POLLERR) { + log_warning("Login POLLERR"); + c->ret = LOGIN_IO_ERROR; + return c->ret; + } - if (ret) + } else if (ret < 0) { + log_error("Login poll error.\n"); + c->ret = LOGIN_FAILED; return c->ret; + } } while (conn->current_stage != ISCSI_FULL_FEATURE_PHASE); c->ret = LOGIN_OK; diff --git a/usr/mgmt_ipc.c b/usr/mgmt_ipc.c index 452121a..813bbca 100644 --- a/usr/mgmt_ipc.c +++ b/usr/mgmt_ipc.c @@ -200,17 +200,6 @@ mgmt_ipc_conn_remove(queue_task_t *qtask) return MGMT_IPC_ERR; } -static mgmt_ipc_err_e -mgmt_ipc_host_set_param(queue_task_t *qtask) -{ - struct ipc_msg_set_host_param *hp = &qtask->req.u.set_host_param; - int err; - - err = iscsi_host_set_param(hp->host_no, hp->param, hp->value); - mgmt_ipc_write_rsp(qtask, err); - return MGMT_IPC_OK; -} - /* * Parse a list of strings, encoded as a 32bit * length followed by the string itself (not necessarily @@ -510,7 +499,6 @@ static mgmt_ipc_fn_t * mgmt_ipc_functions[__MGMT_IPC_MAX_COMMAND] = { [MGMT_IPC_CONFIG_IALIAS] = mgmt_ipc_cfg_initiatoralias, [MGMT_IPC_CONFIG_FILE] = mgmt_ipc_cfg_filename, [MGMT_IPC_IMMEDIATE_STOP] = mgmt_ipc_immediate_stop, -[MGMT_IPC_SET_HOST_PARAM] = mgmt_ipc_host_set_param, [MGMT_IPC_NOTIFY_ADD_NODE] = mgmt_ipc_notify_add_node, [MGMT_IPC_NOTIFY_DEL_NODE] = mgmt_ipc_notify_del_node, [MGMT_IPC_NOTIFY_ADD_PORTAL] = mgmt_ipc_notify_add_portal, diff --git a/usr/mgmt_ipc.h b/usr/mgmt_ipc.h index 401b017..4e19ce1 100644 --- a/usr/mgmt_ipc.h +++ b/usr/mgmt_ipc.h @@ -66,11 +66,10 @@ typedef enum iscsiadm_cmd { MGMT_IPC_SESSION_INFO = 13, MGMT_IPC_ISNS_DEV_ATTR_QUERY = 14, MGMT_IPC_SEND_TARGETS = 15, - MGMT_IPC_SET_HOST_PARAM = 16, - MGMT_IPC_NOTIFY_ADD_NODE = 17, - MGMT_IPC_NOTIFY_DEL_NODE = 18, - MGMT_IPC_NOTIFY_ADD_PORTAL = 19, - MGMT_IPC_NOTIFY_DEL_PORTAL = 20, + MGMT_IPC_NOTIFY_ADD_NODE = 16, + MGMT_IPC_NOTIFY_DEL_NODE = 17, + MGMT_IPC_NOTIFY_ADD_PORTAL = 18, + MGMT_IPC_NOTIFY_DEL_PORTAL = 19, __MGMT_IPC_MAX_COMMAND } iscsiadm_cmd_e; diff --git a/usr/netlink.c b/usr/netlink.c index 06f3d42..e70602d 100644 --- a/usr/netlink.c +++ b/usr/netlink.c @@ -33,7 +33,6 @@ #include "types.h" #include "iscsi_if.h" -#include "iscsid.h" #include "log.h" #include "iscsi_ipc.h" #include "initiator.h" @@ -50,6 +49,7 @@ static void *nlm_sendbuf; static void *nlm_recvbuf; static void *pdu_sendbuf; static void *setparam_buf; +static struct iscsi_ipc_ev_clbk *ipc_ev_clbk; static int ctldev_handle(void); @@ -66,7 +66,8 @@ static int ctldev_handle(void); static int kread(char *data, int count) { - log_debug(7, "in %s", __FUNCTION__); + log_debug(7, "in %s %u %u %p %p", __FUNCTION__, recvlen, count, + data, recvbuf); memcpy(data, recvbuf + recvlen, count); recvlen += count; @@ -716,18 +717,34 @@ kstart_conn(uint64_t transport_handle, uint32_t sid, uint32_t cid, static int krecv_pdu_begin(struct iscsi_conn *conn) { + int rc; + log_debug(7, "in %s", __FUNCTION__); if (recvbuf) { log_error("recv's begin state machine bug?"); return -EIO; } + + if (!conn->recv_context) { + rc = ipc->ctldev_handle(); + if (rc == -ENXIO) + /* event for some other conn */ + return -EAGAIN; + else if (rc < 0) + /* fatal handling error or conn error */ + return rc; + /* + * Session create/destroy event for another conn + */ + if (!conn->recv_context) + return -EAGAIN; + } + recvbuf = conn->recv_context->data + sizeof(struct iscsi_uevent); recvlen = 0; - log_debug(3, "recv PDU began, pdu handle 0x%p", - recvbuf); - + log_debug(3, "recv PDU began, pdu handle %p", recvbuf); return 0; } @@ -744,7 +761,7 @@ krecv_pdu_end(struct iscsi_conn *conn) log_debug(3, "recv PDU finished for pdu handle 0x%p", recvbuf); - iscsi_conn_context_put(conn->recv_context); + ipc_ev_clbk->put_ev_context(conn->recv_context); conn->recv_context = NULL; recvbuf = NULL; return 0; @@ -891,10 +908,23 @@ kget_stats(uint64_t transport_handle, uint32_t sid, uint32_t cid, static void drop_data(struct nlmsghdr *nlh) { - int ev_size; - - ev_size = nlh->nlmsg_len - NLMSG_ALIGN(sizeof(struct nlmsghdr)); - nlpayload_read(ctrl_fd, setparam_buf, ev_size, 0); + int ev_size, read, curr_total; + + curr_total = nlh->nlmsg_len - NLMSG_ALIGN(sizeof(struct nlmsghdr)); + while (curr_total > 0) { + ev_size = curr_total; + if (ev_size > NLM_BUF_DEFAULT_MAX) + ev_size = NLM_BUF_DEFAULT_MAX; + + /* sendbuf will not be used here, so dump data to it */ + read = nlpayload_read(ctrl_fd, nlm_sendbuf, ev_size, 0); + if (read < 0) { + log_error("Could not drop %d bytes of data.\n", + read); + } else if (!read) + break; + curr_total -= read; + } } static int ctldev_handle(void) @@ -905,7 +935,7 @@ static int ctldev_handle(void) iscsi_conn_t *conn = NULL; char nlm_ev[NLMSG_SPACE(sizeof(struct iscsi_uevent))]; struct nlmsghdr *nlh; - struct iscsi_conn_context *conn_context; + struct iscsi_ev_context *ev_context; uint32_t sid = 0, cid = 0; log_debug(7, "in %s", __FUNCTION__); @@ -925,13 +955,15 @@ static int ctldev_handle(void) /* old kernels sent ISCSI_UEVENT_CREATE_SESSION on creation */ case ISCSI_UEVENT_CREATE_SESSION: drop_data(nlh); - iscsi_async_session_creation(ev->r.c_session_ret.host_no, - ev->r.c_session_ret.sid); + if (ipc_ev_clbk->create_session) + ipc_ev_clbk->create_session(ev->r.c_session_ret.host_no, + ev->r.c_session_ret.sid); return 0; case ISCSI_KEVENT_DESTROY_SESSION: drop_data(nlh); - iscsi_async_session_destruction(ev->r.d_session.host_no, - ev->r.d_session.sid); + if (ipc_ev_clbk->destroy_session) + ipc_ev_clbk->destroy_session(ev->r.d_session.host_no, + ev->r.d_session.sid); return 0; case ISCSI_KEVENT_RECV_PDU: sid = ev->r.recv_req.sid; @@ -947,16 +979,30 @@ static int ctldev_handle(void) cid = 0; break; default: - log_error("Unknown kernel event %d. You may want to upgrade " - "your iscsi tools.", ev->type); + if ((ev->type > ISCSI_UEVENT_MAX && ev->type < KEVENT_BASE) || + (ev->type > ISCSI_KEVENT_MAX)) + log_error("Unknown kernel event %d. You may want to " + " upgrade your iscsi tools.", ev->type); + else + /* + * If another app is using the interface we might + * see their + * stuff. Just drop it. + */ + log_debug(7, "Got unknwon event %d. Dropping.", + ev->type); drop_data(nlh); - return -EINVAL; + return 0; } /* verify connection */ session = session_find_by_sid(sid); if (!session) { - log_error("Could not verify connection %d:%d. Dropping " + /* + * this can happen normally when other apps are using the + * nl interface. + */ + log_debug(1, "Could not verify connection %d:%d. Dropping " "event.\n", sid, cid); drop_data(nlh); return -ENXIO; @@ -964,19 +1010,20 @@ static int ctldev_handle(void) conn = &session->conn[0]; ev_size = nlh->nlmsg_len - NLMSG_ALIGN(sizeof(struct nlmsghdr)); - conn_context = iscsi_conn_context_get(conn, ev_size); - if (!conn_context) { + + ev_context = ipc_ev_clbk->get_ev_context(conn, ev_size); + if (!ev_context) { /* retry later */ log_error("Can not allocate memory for receive context."); return -ENOMEM; } log_debug(6, "message real length is %d bytes, recv_handle %p", - nlh->nlmsg_len, conn_context->data); + nlh->nlmsg_len, ev_context->data); - if ((rc = nlpayload_read(ctrl_fd, conn_context->data, + if ((rc = nlpayload_read(ctrl_fd, ev_context->data, ev_size, 0)) < 0) { - iscsi_conn_context_put(conn_context); + ipc_ev_clbk->put_ev_context(ev_context); log_error("can not read from NL socket, error %d", rc); /* retry later */ return rc; @@ -988,26 +1035,28 @@ static int ctldev_handle(void) */ switch (ev->type) { case ISCSI_KEVENT_RECV_PDU: - iscsi_sched_conn_context(conn_context, conn, 0, - EV_CONN_RECV_PDU); + rc = ipc_ev_clbk->sched_ev_context(ev_context, conn, 0, + EV_CONN_RECV_PDU); break; case ISCSI_KEVENT_CONN_ERROR: - memcpy(conn_context->data, &ev->r.connerror.error, + memcpy(ev_context->data, &ev->r.connerror.error, sizeof(ev->r.connerror.error)); - iscsi_sched_conn_context(conn_context, conn, 0, - EV_CONN_ERROR); + rc = ipc_ev_clbk->sched_ev_context(ev_context, conn, 0, + EV_CONN_ERROR); break; case ISCSI_KEVENT_UNBIND_SESSION: - iscsi_sched_conn_context(conn_context, conn, 0, - EV_CONN_STOP); + rc = ipc_ev_clbk->sched_ev_context(ev_context, conn, 0, + EV_CONN_STOP); break; default: - iscsi_conn_context_put(conn_context); + ipc_ev_clbk->put_ev_context(ev_context); log_error("unknown kernel event %d", ev->type); return -EEXIST; } - return 0; + if (rc) + ipc_ev_clbk->put_ev_context(ev_context); + return rc; } static int @@ -1116,3 +1165,8 @@ struct iscsi_ipc nl_ipc = { .recv_pdu_end = krecv_pdu_end, }; struct iscsi_ipc *ipc = &nl_ipc; + +void ipc_register_ev_callback(struct iscsi_ipc_ev_clbk *ev_clbk) +{ + ipc_ev_clbk = ev_clbk; +} diff --git a/usr/transport.c b/usr/transport.c index 2c5224f..7a0cde1 100644 --- a/usr/transport.c +++ b/usr/transport.c @@ -107,6 +107,7 @@ int set_transport_template(struct iscsi_transport *t) } } - log_error("Could not find uspace transport for %s\n", t->name); + log_error("Could not find template for %s. An updated iscsiadm " + "is probably needed.\n", t->name); return ENOSYS; } -- cgit v1.2.1