/* * Copyright (C) 2002 - 2003 Ardis Technologies * Copyright (C) 2007 - 2018 Vladislav Bolkhovitin * Copyright (C) 2007 - 2018 Western Digital Corporation * * 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, version 2 * of the License. * * 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. */ #include #include #include #include #include #include #include #include #include "iscsid.h" static int session_alloc(u32 tid, struct session **psess) { struct session *session; struct target *target = target_find_by_id(tid); int res = 0; if (!target) { log_error("tid %x not found", tid); res = -ENOENT; goto out; } if (!(session = malloc(sizeof(*session)))) { res = -ENOMEM; goto out; } memset(session, 0, sizeof(*session)); session->target = target; INIT_LIST_HEAD(&session->slist); list_add_tail(&session->slist, &target->sessions_list); *psess = session; out: return res; } struct session *session_find_name(u32 tid, const char *iname, union iscsi_sid sid) { struct session *session; struct target *target; if (!(target = target_find_by_id(tid))) { log_error("Target tid %d not found", tid); return NULL; } log_debug(1, "Finding session %s, sid %#" PRIx64, iname, sid.id64); list_for_each_entry(session, &target->sessions_list, slist) { if (!memcmp(sid.id.isid, session->sid.id.isid, 6) && !strcmp(iname, session->initiator)) return session; } return NULL; } struct session *session_find_id(u32 tid, u64 sid) { struct session *session; struct target *target; if (!(target = target_find_by_id(tid))) return NULL; log_debug(1, "Searching for sid %#" PRIx64, sid); list_for_each_entry(session, &target->sessions_list, slist) { if (session->sid.id64 == sid) return session; } return NULL; } static bool session_id_exists(u32 tid, u64 sid, struct session *exclude) { struct session *session; struct target *target; if (!(target = target_find_by_id(tid))) return false; log_debug(1, "Searching for sid %#" PRIx64, sid); list_for_each_entry(session, &target->sessions_list, slist) { if ((session->sid.id64 == sid) && (session != exclude)) return true; } return false; } int session_create(struct connection *conn) { /* We are single threaded, so it doesn't need any protection */ static u16 tsih = 1; struct session *session; int res = 0; res = session_alloc(conn->tid, &session); if (res != 0) { log_error("session_alloc() failed: %d", res); goto out; } session->sid = conn->sid; session->sid.id.tsih = tsih; INIT_LIST_HEAD(&session->conn_list); list_add_tail(&conn->clist, &session->conn_list); conn->sess = session; conn->sess->initiator = strdup(conn->initiator); if (conn->sess->initiator == NULL) { res = -errno; log_error("strdup() failed: %d", res); goto out_free; } while (1) { bool e; e = session_id_exists(conn->tid, session->sid.id64, session); if (!e) break; log_debug(1, "tsih %x already exists", session->sid.id.tsih); session->sid.id.tsih++; } tsih = session->sid.id.tsih + 1; log_debug(1, "sid %#" PRIx64, session->sid.id64); res = kernel_session_create(conn); if (res != 0) goto out_free; out: return res; out_free: list_del_init(&conn->clist); assert(list_empty(&session->conn_list)); session_free(session); conn->sess = NULL; goto out; } void session_free(struct session *session) { log_debug(1, "Freeing session sid %#"PRIx64, session->sid.id64); if (session->target) { struct target *target = session->target; kernel_session_destroy(target->tid, session->sid.id64); target->sessions_count--; log_debug(1, "target %s, sessions_count %d", target->name, target->sessions_count); list_del(&session->slist); } free(session->initiator); free(session); return; } struct connection *conn_find(struct session *session, u16 cid) { struct connection *conn; list_for_each_entry(conn, &session->conn_list, clist) { if (conn->cid == cid) return conn; } return NULL; }