2 * Copyright (C) 2002 - 2003 Ardis Technolgies <roman@ardistech.com>
3 * Copyright (C) 2007 - 2008 Vladislav Bolkhovitin
4 * Copyright (C) 2007 - 2008 CMS Distribution Limited
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation, version 2
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
19 /* target_mutex supposed to be locked */
20 struct iscsi_session *session_lookup(struct iscsi_target *target, u64 sid)
22 struct iscsi_session *session;
24 list_for_each_entry(session, &target->session_list,
26 if (session->sid == sid)
32 /* target_mgmt_mutex supposed to be locked */
33 static int iscsi_session_alloc(struct iscsi_target *target,
34 struct iscsi_kern_session_info *info, struct iscsi_session **result)
38 struct iscsi_session *session;
41 session = kzalloc(sizeof(*session), GFP_KERNEL);
45 session->target = target;
46 session->sid = info->sid;
47 session->sess_param = target->trgt_sess_param;
48 session->max_queued_cmnds = target->trgt_param.queued_cmnds;
49 atomic_set(&session->active_cmds, 0);
51 session->exp_cmd_sn = info->exp_cmd_sn;
53 session->initiator_name = kstrdup(info->initiator_name, GFP_KERNEL);
54 if (!session->initiator_name) {
59 name = kmalloc(strlen(info->user_name) + strlen(info->initiator_name) +
66 if (info->user_name[0] != '\0')
67 sprintf(name, "%s@%s", info->user_name, info->initiator_name);
69 sprintf(name, "%s", info->initiator_name);
71 INIT_LIST_HEAD(&session->conn_list);
72 INIT_LIST_HEAD(&session->pending_list);
74 spin_lock_init(&session->sn_lock);
76 spin_lock_init(&session->cmnd_hash_lock);
77 for (i = 0; i < ARRAY_SIZE(session->cmnd_hash); i++)
78 INIT_LIST_HEAD(&session->cmnd_hash[i]);
80 session->next_ttt = 1;
82 session->scst_sess = scst_register_session(target->scst_tgt, 0,
84 if (session->scst_sess == NULL) {
85 PRINT_ERROR("%s", "scst_register_session() failed");
92 scst_sess_set_tgt_priv(session->scst_sess, session);
94 TRACE_MGMT_DBG("Session %p created: target %p, tid %u, sid %#Lx",
95 session, target, target->tid, info->sid);
102 kfree(session->initiator_name);
109 /* target_mutex supposed to be locked */
110 void sess_enable_reinstated_sess(struct iscsi_session *sess)
112 struct iscsi_conn *c;
116 TRACE_MGMT_DBG("Enabling reinstate successor sess %p", sess);
118 sBUG_ON(!sess->sess_reinstating);
120 list_for_each_entry(c, &sess->conn_list, conn_list_entry) {
121 __iscsi_socket_bind(c);
123 sess->sess_reinstating = 0;
129 /* target_mgmt_mutex supposed to be locked */
130 int session_add(struct iscsi_target *target,
131 struct iscsi_kern_session_info *info)
133 struct iscsi_session *new_sess, *sess, *old_sess;
136 bool reinstatement = false;
138 TRACE_MGMT_DBG("Adding session SID %llx", info->sid);
140 err = iscsi_session_alloc(target, info, &new_sess);
144 mutex_lock(&target->target_mutex);
146 sess = session_lookup(target, info->sid);
148 PRINT_ERROR("Attempt to add session with existing SID %llx",
154 sid = *(union iscsi_sid *)&info->sid;
159 * We need to find the latest session to correctly handle
160 * multi-reinstatements
162 list_for_each_entry_reverse(sess, &target->session_list,
163 session_list_entry) {
164 union iscsi_sid i = *(union iscsi_sid *)&sess->sid;
166 if ((sid.id64 == i.id64) &&
167 (strcmp(info->initiator_name, sess->initiator_name) == 0)) {
168 if (!sess->sess_shutting_down) {
169 /* session reinstatement */
177 list_add_tail(&new_sess->session_list_entry, &target->session_list);
179 if (old_sess != NULL) {
180 reinstatement = true;
182 TRACE_MGMT_DBG("Reinstating sess %p with SID %llx (old %p, "
183 "SID %llx)", new_sess, new_sess->sid, old_sess,
186 new_sess->sess_reinstating = 1;
187 old_sess->sess_reinst_successor = new_sess;
189 target_del_session(old_sess->target, old_sess, 0);
192 mutex_unlock(&target->target_mutex);
196 * Mutex target_mgmt_mutex won't allow to add connections to
197 * the new session after target_mutex was dropped, so it's safe
198 * to replace the initial UA without it. We can't do it under
199 * target_mutex, because otherwise we can establish a
200 * circular locking dependency between target_mutex and
201 * scst_mutex in SCST core (iscsi_report_aen() called by
202 * SCST core under scst_mutex).
204 scst_set_initial_UA(new_sess->scst_sess,
205 SCST_LOAD_SENSE(scst_sense_nexus_loss_UA));
212 mutex_unlock(&target->target_mutex);
214 scst_unregister_session(new_sess->scst_sess, 1, NULL);
215 new_sess->scst_sess = NULL;
217 mutex_lock(&target->target_mutex);
218 session_free(new_sess, false);
219 mutex_unlock(&target->target_mutex);
223 /* target_mutex supposed to be locked */
224 int session_free(struct iscsi_session *session, bool del)
228 TRACE_MGMT_DBG("Freeing session %p (SID %llx)",
229 session, session->sid);
231 sBUG_ON(!list_empty(&session->conn_list));
232 if (unlikely(atomic_read(&session->active_cmds) != 0)) {
233 PRINT_CRIT_ERROR("active_cmds not 0 (%d)!!",
234 atomic_read(&session->active_cmds));
238 for (i = 0; i < ARRAY_SIZE(session->cmnd_hash); i++)
239 sBUG_ON(!list_empty(&session->cmnd_hash[i]));
241 if (session->sess_reinst_successor != NULL)
242 sess_enable_reinstated_sess(session->sess_reinst_successor);
244 if (session->sess_reinstating) {
245 struct iscsi_session *s;
246 TRACE_MGMT_DBG("Freeing being reinstated sess %p", session);
247 list_for_each_entry(s, &session->target->session_list,
248 session_list_entry) {
249 if (s->sess_reinst_successor == session) {
250 s->sess_reinst_successor = NULL;
256 if (session->scst_sess != NULL) {
258 * We must NOT call scst_unregister_session() in the waiting
259 * mode, since we are under target_mutex. Otherwise we can
260 * establish a circular locking dependency between target_mutex
261 * and scst_mutex in SCST core (iscsi_report_aen() called by
262 * SCST core under scst_mutex).
264 scst_unregister_session(session->scst_sess, 0, NULL);
265 session->scst_sess = NULL;
269 list_del(&session->session_list_entry);
271 kfree(session->initiator_name);
277 /* target_mutex supposed to be locked */
278 int session_del(struct iscsi_target *target, u64 sid)
280 struct iscsi_session *session;
282 session = session_lookup(target, sid);
286 if (!list_empty(&session->conn_list)) {
287 PRINT_ERROR("%llu still have connections",
288 (long long unsigned int)session->sid);
292 return session_free(session, true);
295 /* target_mutex supposed to be locked */
296 static void iscsi_session_info_show(struct seq_file *seq,
297 struct iscsi_target *target)
299 struct iscsi_session *session;
301 list_for_each_entry(session, &target->session_list,
302 session_list_entry) {
303 seq_printf(seq, "\tsid:%llx initiator:%s (reinstating %s)\n",
304 (long long unsigned int)session->sid,
305 session->initiator_name,
306 session->sess_reinstating ? "yes" : "no");
307 conn_info_show(seq, session);
312 static int iscsi_session_seq_open(struct inode *inode, struct file *file)
315 res = seq_open(file, &iscsi_seq_op);
317 ((struct seq_file *)file->private_data)->private =
318 iscsi_session_info_show;
322 const struct file_operations session_seq_fops = {
323 .owner = THIS_MODULE,
324 .open = iscsi_session_seq_open,
327 .release = seq_release,