Removed trailing whitespace.
[mirror/scst/.git] / iscsi-scst / kernel / session.c
1 /*
2  *  Copyright (C) 2002 - 2003 Ardis Technolgies <roman@ardistech.com>
3  *  Copyright (C) 2007 - 2009 Vladislav Bolkhovitin
4  *  Copyright (C) 2007 - 2009 ID7 Ltd.
5  *
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
9  *  of the License.
10  *
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.
15  */
16
17 #include "iscsi.h"
18
19 #ifdef CONFIG_SCST_PROC
20 int print_digest_state(char *p, size_t size, unsigned long flags)
21 #else
22 static int print_digest_state(char *p, size_t size, unsigned long flags)
23 #endif
24 {
25         int pos;
26
27         if (DIGEST_NONE & flags)
28                 pos = scnprintf(p, size, "%s", "none");
29         else if (DIGEST_CRC32C & flags)
30                 pos = scnprintf(p, size, "%s", "crc32c");
31         else
32                 pos = scnprintf(p, size, "%s", "unknown");
33
34         return pos;
35 }
36
37 /* target_mutex supposed to be locked */
38 struct iscsi_session *session_lookup(struct iscsi_target *target, u64 sid)
39 {
40         struct iscsi_session *session;
41
42         list_for_each_entry(session, &target->session_list,
43                         session_list_entry) {
44                 if (session->sid == sid)
45                         return session;
46         }
47         return NULL;
48 }
49
50 /* target_mgmt_mutex supposed to be locked */
51 static int iscsi_session_alloc(struct iscsi_target *target,
52         struct iscsi_kern_session_info *info, struct iscsi_session **result)
53 {
54         int err;
55         unsigned int i;
56         struct iscsi_session *session;
57         char *name = NULL;
58
59         session = kzalloc(sizeof(*session), GFP_KERNEL);
60         if (!session)
61                 return -ENOMEM;
62
63         session->target = target;
64         session->sid = info->sid;
65         session->sess_param = target->trgt_sess_param;
66         session->max_queued_cmnds = target->trgt_param.queued_cmnds;
67         atomic_set(&session->active_cmds, 0);
68
69         session->exp_cmd_sn = info->exp_cmd_sn;
70
71         session->initiator_name = kstrdup(info->initiator_name, GFP_KERNEL);
72         if (!session->initiator_name) {
73                 err = -ENOMEM;
74                 goto err;
75         }
76
77         name = kmalloc(strlen(info->user_name) + strlen(info->initiator_name) +
78                         1, GFP_KERNEL);
79         if (name == NULL) {
80                 err = -ENOMEM;
81                 goto err;
82         }
83
84         if (info->user_name[0] != '\0')
85                 sprintf(name, "%s@%s", info->user_name, info->initiator_name);
86         else
87                 sprintf(name, "%s", info->initiator_name);
88
89         INIT_LIST_HEAD(&session->conn_list);
90         INIT_LIST_HEAD(&session->pending_list);
91
92         spin_lock_init(&session->sn_lock);
93
94         spin_lock_init(&session->cmnd_hash_lock);
95         for (i = 0; i < ARRAY_SIZE(session->cmnd_hash); i++)
96                 INIT_LIST_HEAD(&session->cmnd_hash[i]);
97
98         session->next_ttt = 1;
99
100         session->scst_sess = scst_register_session(target->scst_tgt, 0,
101                 name, NULL, NULL);
102         if (session->scst_sess == NULL) {
103                 PRINT_ERROR("%s", "scst_register_session() failed");
104                 err = -ENOMEM;
105                 goto err;
106         }
107
108         kfree(name);
109
110         scst_sess_set_tgt_priv(session->scst_sess, session);
111
112         TRACE_MGMT_DBG("Session %p created: target %p, tid %u, sid %#Lx",
113                 session, target, target->tid, info->sid);
114
115         *result = session;
116         return 0;
117
118 err:
119         if (session) {
120                 kfree(session->initiator_name);
121                 kfree(session);
122                 kfree(name);
123         }
124         return err;
125 }
126
127 /* target_mutex supposed to be locked */
128 void sess_reinst_finished(struct iscsi_session *sess)
129 {
130         struct iscsi_conn *c;
131
132         TRACE_ENTRY();
133
134         TRACE_MGMT_DBG("Enabling reinstate successor sess %p", sess);
135
136         sBUG_ON(!sess->sess_reinstating);
137
138         list_for_each_entry(c, &sess->conn_list, conn_list_entry) {
139                 conn_reinst_finished(c);
140         }
141         sess->sess_reinstating = 0;
142
143         TRACE_EXIT();
144         return;
145 }
146
147 /* target_mgmt_mutex supposed to be locked */
148 int session_add(struct iscsi_target *target,
149         struct iscsi_kern_session_info *info)
150 {
151         struct iscsi_session *new_sess = NULL, *sess, *old_sess;
152         int err = 0;
153         union iscsi_sid sid;
154         bool reinstatement = false;
155
156         TRACE_MGMT_DBG("Adding session SID %llx", info->sid);
157
158         err = iscsi_session_alloc(target, info, &new_sess);
159         if (err != 0)
160                 goto out;
161
162         mutex_lock(&target->target_mutex);
163
164         sess = session_lookup(target, info->sid);
165         if (sess != NULL) {
166                 PRINT_ERROR("Attempt to add session with existing SID %llx",
167                         info->sid);
168                 err = -EEXIST;
169                 goto out_err_unlock;
170         }
171
172         sid = *(union iscsi_sid *)&info->sid;
173         sid.id.tsih = 0;
174         old_sess = NULL;
175
176         /*
177          * We need to find the latest session to correctly handle
178          * multi-reinstatements
179          */
180         list_for_each_entry_reverse(sess, &target->session_list,
181                         session_list_entry) {
182                 union iscsi_sid i = *(union iscsi_sid *)&sess->sid;
183                 i.id.tsih = 0;
184                 if ((sid.id64 == i.id64) &&
185                     (strcmp(info->initiator_name, sess->initiator_name) == 0)) {
186                         if (!sess->sess_shutting_down) {
187                                 /* session reinstatement */
188                                 old_sess = sess;
189                         }
190                         break;
191                 }
192         }
193         sess = NULL;
194
195         list_add_tail(&new_sess->session_list_entry, &target->session_list);
196
197         if (old_sess != NULL) {
198                 reinstatement = true;
199
200                 TRACE_MGMT_DBG("Reinstating sess %p with SID %llx (old %p, "
201                         "SID %llx)", new_sess, new_sess->sid, old_sess,
202                         old_sess->sid);
203
204                 new_sess->sess_reinstating = 1;
205                 old_sess->sess_reinst_successor = new_sess;
206
207                 target_del_session(old_sess->target, old_sess, 0);
208         }
209
210         mutex_unlock(&target->target_mutex);
211
212         if (reinstatement) {
213                 /*
214                  * Mutex target_mgmt_mutex won't allow to add connections to
215                  * the new session after target_mutex was dropped, so it's safe
216                  * to replace the initial UA without it. We can't do it under
217                  * target_mutex, because otherwise we can establish a
218                  * circular locking dependency between target_mutex and
219                  * scst_mutex in SCST core (iscsi_report_aen() called by
220                  * SCST core under scst_mutex).
221                  */
222                 scst_set_initial_UA(new_sess->scst_sess,
223                         SCST_LOAD_SENSE(scst_sense_nexus_loss_UA));
224         }
225
226 out:
227         return err;
228
229 out_err_unlock:
230         mutex_unlock(&target->target_mutex);
231
232         scst_unregister_session(new_sess->scst_sess, 1, NULL);
233         new_sess->scst_sess = NULL;
234
235         mutex_lock(&target->target_mutex);
236         session_free(new_sess, false);
237         mutex_unlock(&target->target_mutex);
238         goto out;
239 }
240
241 static void __session_free(struct iscsi_session *session)
242 {
243         kfree(session->initiator_name);
244         kfree(session);
245 }
246
247 static void iscsi_unreg_sess_done(struct scst_session *scst_sess)
248 {
249         struct iscsi_session *session;
250
251         TRACE_ENTRY();
252
253         session = (struct iscsi_session *)scst_sess_get_tgt_priv(scst_sess);
254
255         session->scst_sess = NULL;
256         __session_free(session);
257
258         TRACE_EXIT();
259         return;
260 }
261
262 /* target_mutex supposed to be locked */
263 int session_free(struct iscsi_session *session, bool del)
264 {
265         unsigned int i;
266
267         TRACE_MGMT_DBG("Freeing session %p (SID %llx)",
268                 session, session->sid);
269
270         sBUG_ON(!list_empty(&session->conn_list));
271         if (unlikely(atomic_read(&session->active_cmds) != 0)) {
272                 PRINT_CRIT_ERROR("active_cmds not 0 (%d)!!",
273                         atomic_read(&session->active_cmds));
274                 sBUG();
275         }
276
277         for (i = 0; i < ARRAY_SIZE(session->cmnd_hash); i++)
278                 sBUG_ON(!list_empty(&session->cmnd_hash[i]));
279
280         if (session->sess_reinst_successor != NULL)
281                 sess_reinst_finished(session->sess_reinst_successor);
282
283         if (session->sess_reinstating) {
284                 struct iscsi_session *s;
285                 TRACE_MGMT_DBG("Freeing being reinstated sess %p", session);
286                 list_for_each_entry(s, &session->target->session_list,
287                                                 session_list_entry) {
288                         if (s->sess_reinst_successor == session) {
289                                 s->sess_reinst_successor = NULL;
290                                 break;
291                         }
292                 }
293         }
294
295         if (del)
296                 list_del(&session->session_list_entry);
297
298         if (session->scst_sess != NULL) {
299                 /*
300                  * We must NOT call scst_unregister_session() in the waiting
301                  * mode, since we are under target_mutex. Otherwise we can
302                  * establish a circular locking dependency between target_mutex
303                  * and scst_mutex in SCST core (iscsi_report_aen() called by
304                  * SCST core under scst_mutex).
305                  */
306                 scst_unregister_session(session->scst_sess, 0,
307                         iscsi_unreg_sess_done);
308         } else
309                 __session_free(session);
310
311         return 0;
312 }
313
314 /* target_mutex supposed to be locked */
315 int session_del(struct iscsi_target *target, u64 sid)
316 {
317         struct iscsi_session *session;
318
319         session = session_lookup(target, sid);
320         if (!session)
321                 return -ENOENT;
322
323         if (!list_empty(&session->conn_list)) {
324                 PRINT_ERROR("%llu still have connections",
325                             (long long unsigned int)session->sid);
326                 return -EBUSY;
327         }
328
329         return session_free(session, true);
330 }
331
332 #ifdef CONFIG_SCST_PROC
333
334 /* target_mutex supposed to be locked */
335 static void iscsi_session_info_show(struct seq_file *seq,
336                                     struct iscsi_target *target)
337 {
338         struct iscsi_session *session;
339
340         list_for_each_entry(session, &target->session_list,
341                             session_list_entry) {
342                 seq_printf(seq, "\tsid:%llx initiator:%s (reinstating %s)\n",
343                         (long long unsigned int)session->sid,
344                         session->initiator_name,
345                         session->sess_reinstating ? "yes" : "no");
346                 conn_info_show(seq, session);
347         }
348         return;
349 }
350
351 static int iscsi_session_seq_open(struct inode *inode, struct file *file)
352 {
353         int res;
354         res = seq_open(file, &iscsi_seq_op);
355         if (!res)
356                 ((struct seq_file *)file->private_data)->private =
357                         iscsi_session_info_show;
358         return res;
359 }
360
361 const struct file_operations session_seq_fops = {
362         .owner          = THIS_MODULE,
363         .open           = iscsi_session_seq_open,
364         .read           = seq_read,
365         .llseek         = seq_lseek,
366         .release        = seq_release,
367 };
368
369 #else /* CONFIG_SCST_PROC */
370
371 static ssize_t iscsi_sess_sid_show(struct kobject *kobj,
372         struct kobj_attribute *attr, char *buf)
373 {
374         int pos;
375         struct scst_session *scst_sess;
376         struct iscsi_session *sess;
377
378         TRACE_ENTRY();
379
380         scst_sess = container_of(kobj, struct scst_session, sess_kobj);
381         sess = (struct iscsi_session *)scst_sess_get_tgt_priv(scst_sess);
382
383         pos = sprintf(buf, "%llx\n", sess->sid);
384
385         TRACE_EXIT_RES(pos);
386         return pos;
387 }
388
389 static struct kobj_attribute iscsi_sess_sid_attr =
390         __ATTR(sid, S_IRUGO, iscsi_sess_sid_show, NULL);
391
392 static ssize_t iscsi_sess_reinstating_show(struct kobject *kobj,
393         struct kobj_attribute *attr, char *buf)
394 {
395         int pos;
396         struct scst_session *scst_sess;
397         struct iscsi_session *sess;
398
399         TRACE_ENTRY();
400
401         scst_sess = container_of(kobj, struct scst_session, sess_kobj);
402         sess = (struct iscsi_session *)scst_sess_get_tgt_priv(scst_sess);
403
404         pos = sprintf(buf, "%d\n", sess->sess_reinstating ? 1 : 0);
405
406         TRACE_EXIT_RES(pos);
407         return pos;
408 }
409
410 static struct kobj_attribute iscsi_sess_reinstating_attr =
411         __ATTR(reinstating, S_IRUGO, iscsi_sess_reinstating_show, NULL);
412
413 static ssize_t iscsi_sess_hdigest_show(struct kobject *kobj,
414         struct kobj_attribute *attr, char *buf)
415 {
416         int pos;
417         struct scst_session *scst_sess;
418         struct iscsi_session *sess;
419
420         TRACE_ENTRY();
421
422         scst_sess = container_of(kobj, struct scst_session, sess_kobj);
423         sess = (struct iscsi_session *)scst_sess_get_tgt_priv(scst_sess);
424
425         pos = print_digest_state(buf, SCST_SYSFS_BLOCK_SIZE,
426                 sess->sess_param.header_digest);
427
428         TRACE_EXIT_RES(pos);
429         return pos;
430 }
431
432 static struct kobj_attribute iscsi_sess_hdigest_attr =
433         __ATTR(hdigest, S_IRUGO, iscsi_sess_hdigest_show, NULL);
434
435 static ssize_t iscsi_sess_ddigest_show(struct kobject *kobj,
436         struct kobj_attribute *attr, char *buf)
437 {
438         int pos;
439         struct scst_session *scst_sess;
440         struct iscsi_session *sess;
441
442         TRACE_ENTRY();
443
444         scst_sess = container_of(kobj, struct scst_session, sess_kobj);
445         sess = (struct iscsi_session *)scst_sess_get_tgt_priv(scst_sess);
446
447         pos = print_digest_state(buf, SCST_SYSFS_BLOCK_SIZE,
448                 sess->sess_param.data_digest);
449
450         TRACE_EXIT_RES(pos);
451         return pos;
452 }
453
454 static struct kobj_attribute iscsi_sess_ddigest_attr =
455         __ATTR(ddigest, S_IRUGO, iscsi_sess_ddigest_show, NULL);
456
457 const struct attribute *iscsi_sess_attrs[] = {
458         &iscsi_sess_sid_attr.attr,
459         &iscsi_sess_reinstating_attr.attr,
460         &iscsi_sess_hdigest_attr.attr,
461         &iscsi_sess_ddigest_attr.attr,
462         NULL,
463 };
464
465 #endif /* CONFIG_SCST_PROC */