diff options
author | Mike Christie <michaelc@cs.wisc.edu> | 2010-03-22 17:45:14 -0500 |
---|---|---|
committer | Mike Christie <michaelc@cs.wisc.edu> | 2010-03-22 17:45:14 -0500 |
commit | 7f6a05718a11a4bf0a241aa657b638bfb370b819 (patch) | |
tree | ed387ef085d6f181fe4017e850e22be4437128f8 | |
parent | c94bb3206d8816f9d13c8d971513202d94cbb9ea (diff) | |
download | open-iscsi-7f6a05718a11a4bf0a241aa657b638bfb370b819.tar.gz |
iscsid: support discovery daemon auto logout
With this patch, if the sendtargets or isns
daemon detects that a target is no longer sent
it will logout the session.
-rw-r--r-- | README | 9 | ||||
-rw-r--r-- | include/list.h | 12 | ||||
-rw-r--r-- | usr/discoveryd.c | 233 | ||||
-rw-r--r-- | usr/idbm.c | 14 | ||||
-rw-r--r-- | usr/idbm.h | 3 | ||||
-rw-r--r-- | usr/initiator.c | 15 | ||||
-rw-r--r-- | usr/iscsi_sysfs.c | 2 | ||||
-rw-r--r-- | usr/iscsiadm.c | 10 | ||||
-rw-r--r-- | usr/session_mgmt.c | 66 | ||||
-rw-r--r-- | usr/session_mgmt.h | 5 |
10 files changed, 237 insertions, 132 deletions
@@ -4,8 +4,7 @@ ================================================================= - May 20, 2009 - + March 22, 2010 Contents ======== @@ -852,8 +851,10 @@ being set. If an address or addresses are set, iscsid will perform discovery to the address every poll_interval seconds, and it will log into any portals found from the discovery source using the ifaces in /etc/iscsi/ifaces. -Note: iscsid will login into new portals, but does not yet support logging -out of portals that are no longer returned during discovery. +Note that for iSNS the poll_interval does not have to be set. If not set, +iscsid will only perform rediscovery when it gets a SCN from the server. +SCNs are not supported when using the Microsoft or SLES iSNS server. If +using one of them you should set the poll_interval. See the iscsid.conf for more examples. diff --git a/include/list.h b/include/list.h index a59ca00..cccc3c3 100644 --- a/include/list.h +++ b/include/list.h @@ -90,4 +90,16 @@ static inline void list_del_init(struct list_head *entry) INIT_LIST_HEAD(entry); } +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + #endif diff --git a/usr/discoveryd.c b/usr/discoveryd.c index 4d01b7a..07c2b81 100644 --- a/usr/discoveryd.c +++ b/usr/discoveryd.c @@ -53,7 +53,9 @@ #define DISC_DEF_POLL_INVL 30 -static LIST_HEAD(isns_nodes); +static LIST_HEAD(iscsi_targets); + +static LIST_HEAD(isns_initiators); static LIST_HEAD(isns_refresh_list); static char *isns_entity_id = NULL; static uint32_t isns_refresh_interval; @@ -64,6 +66,106 @@ static void isns_reg_refresh_by_eid_qry(void *data); typedef void (do_disc_and_login_fn)(const char *def_iname, char *addr, int port, int poll_inval); +static int logout_stale_session(void *data, struct list_head *list, + struct session_info *info) +{ + struct list_head *stale_rec_list = data; + struct node_rec *rec; + + list_for_each_entry(rec, stale_rec_list, list) { + if (iscsi_match_session(rec, info)) + return iscsi_logout_portal(info, list); + } + return -1; +} + +static void free_curr_targets(void) +{ + struct node_rec *rec, *tmp_rec; + + list_for_each_entry_safe(rec, tmp_rec, &iscsi_targets, list) { + list_del(&rec->list); + free(rec); + } +} + +/* + * update_sessions - login/logout sessions + * @new_rec_list: new target portals recs bound to ifaces + * @targetname: if set we only update sessions for this target + * @iname: if set we only update session for that initiator + * + * This will login/logout of portals. When it returns the recs on + * new_rec_list will be freed or put on the iscsi_targets list. + * + * FIXME: if we are hitting a per problem this may be it. With targets + * that do a target per lun this could get ugly. + */ +static void update_sessions(struct list_head *new_rec_list, + const char *targetname, const char *iname) +{ + struct node_rec *rec, *tmp_rec; + struct list_head stale_rec_list; + int nr_found; + + INIT_LIST_HEAD(&stale_rec_list); + /* + * Check if a target portal is no longer being sent. + * Note: Due to how we reread ifaces this will also detect + * changes in ifaces being access through portals. + */ + list_for_each_entry_safe(rec, tmp_rec, &iscsi_targets, list) { + log_debug(7, "Trying to match %s %s to %s %s %s", + targetname, iname, rec->name, rec->conn[0].address, + rec->iface.name); + if (targetname && strcmp(rec->name, targetname)) + continue; + + if (iname) { + if (strlen(rec->iface.iname) && + strcmp(rec->iface.iname, iname)) + continue; + else if (strcmp(iname, isns_config.ic_source_name)) + continue; + } + + log_debug(5, "Matched %s %s, checking if in new targets.", + targetname, iname); + if (!idbm_find_rec_in_list(new_rec_list, rec->name, + rec->conn[0].address, + rec->conn[0].port, &rec->iface)) { + log_debug(5, "Not found. Marking for logout"); + list_move_tail(&rec->list, &stale_rec_list); + } + } + + list_for_each_entry_safe(rec, tmp_rec, new_rec_list, list) { + if (!iscsi_check_for_running_session(rec)) + iscsi_login_portal_nowait(rec); + + if (!idbm_find_rec_in_list(&iscsi_targets, rec->name, + rec->conn[0].address, + rec->conn[0].port, &rec->iface)) { + log_debug(5, "%s %s %s %s not on curr target list. " + "Adding.", rec->name, rec->conn[0].address, + rec->iface.name, rec->iface.iname); + list_move_tail(&rec->list, &iscsi_targets); + } else { + list_del(&rec->list); + free(rec); + } + } + + if (!list_empty(&stale_rec_list)) { + iscsi_logout_portals(&stale_rec_list, &nr_found, 0, + logout_stale_session); + list_for_each_entry_safe(rec, tmp_rec, &stale_rec_list, list) { + list_del(&rec->list); + free(rec); + } + } +} + static void do_disc_to_addrs(const char *def_iname, char *disc_addrs, int poll_inval, do_disc_and_login_fn *do_disc_and_login) @@ -130,58 +232,12 @@ static void __discoveryd_start(const char *def_iname, char *addr_cfg_str, free(disc_addrs); } -/* iSNS */ -static void do_isns_disc_and_login(char *disc_addr, int port) -{ - discovery_rec_t drec; - struct list_head rec_list, setup_ifaces; - int rc, nr_found; - struct node_rec *rec, *tmp_rec; - struct iface_rec *iface, *tmp_iface; - - log_debug(1, "iSNS: do_isns_disc_and_login to %s,%d.", - disc_addr, port); - - INIT_LIST_HEAD(&rec_list); - INIT_LIST_HEAD(&setup_ifaces); - - drec.type = DISCOVERY_TYPE_ISNS; - strlcpy(drec.address, disc_addr, sizeof(drec.address)); - drec.port = port; - - iface_link_ifaces(&setup_ifaces); - rc = idbm_bind_ifaces_to_nodes(discovery_isns, &drec, - &setup_ifaces, &rec_list); - if (rc) { - log_error("Could not perform iSNS DevAttrQuery to %s.", - disc_addr); - goto free_ifaces; - } - - list_for_each_entry_safe(rec, tmp_rec, &rec_list, list) { - if (iscsi_check_for_running_session(rec)) { - list_del(&rec->list); - free(rec); - } - - /* no need to retry since the disc daemon will retry */ - rec->session.initial_login_retry_max = 0; - } - - iscsi_login_portals(NULL, &nr_found, &rec_list, iscsi_login_portal); - -free_ifaces: - list_for_each_entry_safe(iface, tmp_iface, &setup_ifaces, list) { - list_del(&iface->list); - free(iface); - } -} - struct isns_node_list { isns_source_t *source; struct list_head list; }; +/* iSNS */ static int isns_build_objs(isns_portal_info_t *portal_info, isns_object_list_t *objs) { @@ -262,7 +318,7 @@ static int isns_build_objs(isns_portal_info_t *portal_info, } } - list_for_each_entry(node, &isns_nodes, list) { + list_for_each_entry(node, &isns_initiators, list) { inode = isns_create_storage_node2(node->source, ISNS_ISCSI_INITIATOR_MASK, NULL); @@ -310,13 +366,12 @@ static int isns_query_node(void *data, struct iface_rec *iface, return discovery_isns_query(NULL, iname, qry_data->targetname, recs); } -static int __isns_disc_new_portals(const char *targetname, const char *iname) +static int isns_disc_new_portals(const char *targetname, const char *iname) { struct list_head ifaces, rec_list; struct iface_rec *iface, *tmp_iface; - struct node_rec *rec, *tmp_rec; struct isns_qry_data qry_data; - int nr_found = 0, rc; + int rc; INIT_LIST_HEAD(&rec_list); INIT_LIST_HEAD(&ifaces); @@ -332,15 +387,7 @@ static int __isns_disc_new_portals(const char *targetname, const char *iname) targetname); goto free_ifaces; } - - list_for_each_entry_safe(rec, tmp_rec, &rec_list, list) { - if (iscsi_check_for_running_session(rec)) { - list_del(&rec->list); - free(rec); - } - } - - iscsi_login_portals(NULL, &nr_found, &rec_list, iscsi_login_portal); + update_sessions(&rec_list, targetname, iname); rc = 0; free_ifaces: @@ -367,7 +414,7 @@ static void isns_reg_refresh_with_disc(void *data) * Some servers do not support SCNs so we ping * the server by doing discovery. */ - rc = __isns_disc_new_portals(NULL, NULL); + rc = isns_disc_new_portals(NULL, NULL); if (rc) { log_debug(4, "Registration refresh using DevAttrQuery " "failed (retires %d) err %d", retries, rc); @@ -641,7 +688,7 @@ static int isns_register_objs(isns_client_t *clnt, isns_object_list_t *objs, if (rc) goto free_reg; - list_for_each_entry(node, &isns_nodes, list) { + list_for_each_entry(node, &isns_initiators, list) { isns_simple_free(reg); reg = isns_create_scn_registration2(clnt, ISNS_SCN_OBJECT_UPDATED_MASK | @@ -723,7 +770,7 @@ static isns_source_t *isns_lookup_node(char *iname) { struct isns_node_list *node; - list_for_each_entry(node, &isns_nodes, list) { + list_for_each_entry(node, &isns_initiators, list) { if (!strcmp(iname, isns_source_name(node->source))) return node->source; } @@ -765,7 +812,7 @@ static int isns_create_node_list(const char *def_iname) rc = ENOMEM; goto fail; } - list_add_tail(&node->list, &isns_nodes); + list_add_tail(&node->list, &isns_initiators); } list_for_each_entry(iface, &ifaces, list) { @@ -776,14 +823,14 @@ static int isns_create_node_list(const char *def_iname) rc = ENOMEM; goto fail; } - list_add_tail(&node->list, &isns_nodes); + list_add_tail(&node->list, &isns_initiators); } } /* fix me */ rc = 0; goto done; fail: - list_for_each_entry_safe(node, tmp_node, &isns_nodes, list) { + list_for_each_entry_safe(node, tmp_node, &isns_initiators, list) { list_del(&node->list); free(node); } @@ -796,37 +843,13 @@ done: return rc; } -static void isns_disc_new_portals(const char *targetname, const char *iname) -{ - pid_t pid; - - pid = fork(); - if (pid < 0) { - log_error("Could not fork process to discover new portals."); - return; - } else if (pid > 0) { - log_debug(1, "iSNS SCN handler for initiator %s (target %s). " - "pid=%d", iname, targetname, pid); - reap_inc(); - return; - } - - __isns_disc_new_portals(targetname, iname); - exit(0); -} - static void isns_scn_callback(isns_db_t *db, uint32_t bitmap, isns_object_template_t *node_type, const char *node_name, const char *dst_name) { log_error("SCN for initiator: %s (Target: %s, Event: %s.)", dst_name, node_name, isns_event_string(bitmap)); - - if (bitmap & ISNS_SCN_OBJECT_REMOVED_MASK) { - log_error("Auto removal not supported. Manually logout of " - "portals on %s", node_name); - } else if (bitmap & ISNS_SCN_OBJECT_ADDED_MASK) - isns_disc_new_portals(node_name, dst_name); + isns_disc_new_portals(node_name, dst_name); } static void isns_clear_refresh_list(void) @@ -876,7 +899,7 @@ static int isns_scn_recv(isns_server_t *svr, isns_socket_t *svr_sock, continue; } - __isns_disc_new_portals(NULL, NULL); + isns_disc_new_portals(NULL, NULL); if (!poll_inval) break; isns_register_nodes = 0; @@ -931,13 +954,13 @@ static int isns_eventd(const char *def_iname, char *disc_addr, int port, int rc = 0; isns_create_node_list(def_iname); - if (list_empty(&isns_nodes)) { + if (list_empty(&isns_initiators)) { log_error("iSNS registration failed. Initiatorname not set."); return EINVAL; } /* use def_iname or if not set the first iface's iname for the src */ - node = list_entry(isns_nodes.next, struct isns_node_list, list); + node = list_entry(isns_initiators.next, struct isns_node_list, list); isns_assign_string(&isns_config.ic_source_name, isns_source_name(node->source)); isns_config.ic_security = 0; @@ -976,8 +999,9 @@ static int isns_eventd(const char *def_iname, char *disc_addr, int port, isns_cancel_refresh_timers(); fail: isns_clear_refresh_list(); + free_curr_targets(); - list_for_each_entry_safe(node, tmp_node, &isns_nodes, list) { + list_for_each_entry_safe(node, tmp_node, &isns_initiators, list) { list_del(&node->list); free(node); } @@ -1011,9 +1035,8 @@ static void __do_st_disc_and_login(char *disc_addr, int port) { discovery_rec_t drec; struct list_head rec_list, setup_ifaces; - int rc, nr_found; - struct node_rec *rec, *tmp_rec; struct iface_rec *iface, *tmp_iface; + int rc; INIT_LIST_HEAD(&rec_list); INIT_LIST_HEAD(&setup_ifaces); @@ -1045,17 +1068,7 @@ static void __do_st_disc_and_login(char *disc_addr, int port) goto free_ifaces; } - list_for_each_entry_safe(rec, tmp_rec, &rec_list, list) { - if (iscsi_check_for_running_session(rec)) { - list_del(&rec->list); - free(rec); - } - - /* no need to retry since the disc daemon will retry */ - rec->session.initial_login_retry_max = 0; - } - - iscsi_login_portals(NULL, &nr_found, &rec_list, iscsi_login_portal); + update_sessions(&rec_list, NULL, NULL); free_ifaces: list_for_each_entry_safe(iface, tmp_iface, &setup_ifaces, list) { @@ -1075,6 +1088,8 @@ static void do_st_disc_and_login(const char *def_iname, char *disc_addr, if (!poll_inval) break; } while (!sleep(poll_inval)); + + free_curr_targets(); } void discoveryd_start(const char *def_iname) @@ -2239,3 +2239,17 @@ void idbm_node_setup_defaults(node_rec_t *rec) iface_setup_defaults(&rec->iface); } + +struct node_rec * +idbm_find_rec_in_list(struct list_head *rec_list, char *targetname, char *addr, + int port, struct iface_rec *iface) +{ + struct node_rec *rec; + + list_for_each_entry(rec, rec_list, list) { + if (__iscsi_match_session(rec, targetname, addr, port, iface)) + return rec; + } + + return NULL; +} @@ -137,6 +137,9 @@ extern int idbm_rec_read(node_rec_t *out_rec, char *target_name, extern int idbm_node_set_param(void *data, node_rec_t *rec); extern int idbm_discovery_set_param(void *data, discovery_rec_t *rec); extern void idbm_node_setup_defaults(node_rec_t *rec); +extern struct node_rec *idbm_find_rec_in_list(struct list_head *rec_list, + char *targetname, char *addr, + int port, struct iface_rec *iface); /* lower level idbm functions for use by iface.c */ extern void idbm_recinfo_config(recinfo_t *info, FILE *f); diff --git a/usr/initiator.c b/usr/initiator.c index 5c7270a..1c9d8b6 100644 --- a/usr/initiator.c +++ b/usr/initiator.c @@ -677,6 +677,13 @@ cleanup: } } + log_warning("Connection%d:%d to [target: %s, portal: %s,%d] " + "through [iface: %s] is shutdown.", + session->id, conn->id, session->nrec.name, + session->nrec.conn[conn->id].address, + session->nrec.conn[conn->id].port, + session->nrec.iface.name); + mgmt_ipc_write_rsp(qtask, err); conn_delete_timers(conn); __session_destroy(session); @@ -1486,8 +1493,12 @@ setup_full_feature_phase(iscsi_conn_t *conn) if (conn->id == 0) session_scan_host(session, session->hostno, c->qtask); - log_warning("connection%d:%d is operational now", - session->id, conn->id); + log_warning("Connection%d:%d to [target: %s, portal: %s,%d] " + "through [iface: %s] is operational now", + session->id, conn->id, session->nrec.name, + session->nrec.conn[conn->id].address, + session->nrec.conn[conn->id].port, + session->nrec.iface.name); } else { session->sync_qtask = NULL; diff --git a/usr/iscsi_sysfs.c b/usr/iscsi_sysfs.c index 77069cd..31a8c88 100644 --- a/usr/iscsi_sysfs.c +++ b/usr/iscsi_sysfs.c @@ -768,6 +768,8 @@ int iscsi_sysfs_for_each_session(void *data, int *nr_found, if (rc) { log_error("could not find session info for %s", namelist[i]->d_name); + /* raced. session was shutdown while looping */ + rc = 0; continue; } diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c index fe9cad7..df55105 100644 --- a/usr/iscsiadm.c +++ b/usr/iscsiadm.c @@ -364,7 +364,7 @@ logout_by_startup(char *mode) return EINVAL; } - return iscsi_logout_portals(mode, &nr_found, __logout_by_startup); + return iscsi_logout_portals(mode, &nr_found, 1, __logout_by_startup); } /* @@ -404,7 +404,7 @@ login_by_startup(char *mode) INIT_LIST_HEAD(&rec_list); rc = idbm_for_each_rec(&nr_found, &rec_list, link_recs); - err = iscsi_login_portals(mode, &nr_found, &rec_list, + err = iscsi_login_portals(mode, &nr_found, 1, &rec_list, __login_by_startup); if (err && !rc) rc = err; @@ -499,7 +499,7 @@ static int login_portals(struct node_rec *pattern_rec) INIT_LIST_HEAD(&rec_list); ret = for_each_rec(pattern_rec, &rec_list, link_recs); - err = iscsi_login_portals(NULL, &nr_found, &rec_list, + err = iscsi_login_portals(NULL, &nr_found, 1, &rec_list, iscsi_login_portal); if (err && !ret) ret = err; @@ -883,7 +883,7 @@ exec_disc_op_on_recs(discovery_rec_t *drec, struct list_head *rec_list, if (!do_login) return 0; - err = iscsi_login_portals(NULL, &found, rec_list, + err = iscsi_login_portals(NULL, &found, 1, rec_list, iscsi_login_portal); if (err && !rc) rc = err; @@ -1265,7 +1265,7 @@ static int exec_node_op(int op, int do_login, int do_logout, if (do_logout) { int nr_found; - if (iscsi_logout_portals(rec, &nr_found, + if (iscsi_logout_portals(rec, &nr_found, 1, iscsi_logout_matched_portal)) rc = -1; goto out; diff --git a/usr/session_mgmt.c b/usr/session_mgmt.c index 99543b3..46189e1 100644 --- a/usr/session_mgmt.c +++ b/usr/session_mgmt.c @@ -22,6 +22,7 @@ #include <stdlib.h> #include <stdio.h> #include <errno.h> +#include <unistd.h> #include "idbm.h" #include "list.h" @@ -53,6 +54,23 @@ struct iscsid_async_req { int fd; }; +/** + * iscsid_reqs_close - close open async requests + * @list: list of async reqs + * + * This just closes the socket to the daemon. + */ +static void iscsid_reqs_close(struct list_head *list) +{ + struct iscsid_async_req *tmp, *curr; + + list_for_each_entry_safe(curr, tmp, list, list) { + close(curr->fd); + list_del(&curr->list); + free(curr); + } +} + static int iscsid_login_reqs_wait(struct list_head *list) { struct iscsid_async_req *tmp, *curr; @@ -123,17 +141,38 @@ int iscsi_login_portal(void *data, struct list_head *list, struct node_rec *rec) } /** + * iscsi_login_portal_nowait - request iscsid to login to portal + * @rec: portal rec to log into + * + * This sends the login request, but does not wait for the result. + */ +int iscsi_login_portal_nowait(struct node_rec *rec) +{ + struct list_head list; + int err; + + INIT_LIST_HEAD(&list); + + err = iscsi_login_portal(NULL, &list, rec); + if (err > 0) + return err; + iscsid_reqs_close(&list); + return 0; +} + +/** * iscsi_login_portals - login into portals on @rec_list, * @data: data to pass to login_fn * @nr_found: returned with number of portals logged into + * @wait: bool indicating if the fn should wait for the result * @rec_list: list of portals to log into * @login_fn: list iter function * * This will loop over the list of portals and login. It * will attempt to login asynchronously, and then wait for - * them to complete. + * them to complete if wait is set. */ -int iscsi_login_portals(void *data, int *nr_found, +int iscsi_login_portals(void *data, int *nr_found, int wait, struct list_head *rec_list, int (* login_fn)(void *, struct list_head *, struct node_rec *)) @@ -153,9 +192,12 @@ int iscsi_login_portals(void *data, int *nr_found, (*nr_found)++; } - err = iscsid_login_reqs_wait(&login_list); - if (err && !ret) - ret = err; + if (wait) { + err = iscsid_login_reqs_wait(&login_list); + if (err && !ret) + ret = err; + } else + iscsid_reqs_close(&login_list); list_for_each_entry_safe(curr_rec, tmp, rec_list, list) { list_del(&curr_rec->list); @@ -256,13 +298,14 @@ int iscsi_logout_portal(struct session_info *info, struct list_head *list) * iscsi_logout_portals - logout portals * @data: data to pass to iter logout_fn * @nr_found: number of sessions logged out + * @wait: bool indicating if the fn should wait for the result * @logout_fn: logout iter function * * This will loop over the list of sessions and run the logout fn * on them. It will attempt to logout asynchronously, and then wait for - * them to complete. + * them to complete if wait is set. */ -int iscsi_logout_portals(void *data, int *nr_found, +int iscsi_logout_portals(void *data, int *nr_found, int wait, int (*logout_fn)(void *, struct list_head *, struct session_info *)) { @@ -294,9 +337,12 @@ int iscsi_logout_portals(void *data, int *nr_found, (*nr_found)++; } - err = iscsid_logout_reqs_wait(&logout_list); - if (err) - ret = err; + if (wait) { + err = iscsid_logout_reqs_wait(&logout_list); + if (err) + ret = err; + } else + iscsid_reqs_close(&logout_list); session_info_free_list(&session_list); return ret; diff --git a/usr/session_mgmt.h b/usr/session_mgmt.h index c310d49..b800fd7 100644 --- a/usr/session_mgmt.h +++ b/usr/session_mgmt.h @@ -7,13 +7,14 @@ struct session_info; extern int iscsi_login_portal(void *data, struct list_head *list, struct node_rec *rec); -extern int iscsi_login_portals(void *data, int *nr_found, +extern int iscsi_login_portal_nowait(struct node_rec *rec); +extern int iscsi_login_portals(void *data, int *nr_found, int wait, struct list_head *rec_list, int (* login_fn)(void *, struct list_head *, struct node_rec *)); extern int iscsi_logout_portal(struct session_info *info, struct list_head *list); -extern int iscsi_logout_portals(void *data, int *nr_found, +extern int iscsi_logout_portals(void *data, int *nr_found, int wait, int (*logout_fn)(void *, struct list_head *, struct session_info *)); extern int iscsi_check_for_running_session(struct node_rec *rec); |