0e3ef40e72c325c6556b52fbe1078cd87a86318c
[mirror/scst/.git] / iscsi-scst / kernel / session.c
1 /*
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
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 /* target_mutex supposed to be locked */
20 struct iscsi_session *session_lookup(struct iscsi_target *target, u64 sid)
21 {
22         struct iscsi_session *session;
23
24         list_for_each_entry(session, &target->session_list,
25                         session_list_entry) {
26                 if ((session->sid == sid) && !session->shutting_down)
27                         return session;
28         }
29         return NULL;
30 }
31
32 /* target_mutex supposed to be locked */
33 static int iscsi_session_alloc(struct iscsi_target *target,
34                                struct session_info *info)
35 {
36         int err;
37         unsigned int i;
38         struct iscsi_session *session;
39         char *name = NULL;
40
41         session = kzalloc(sizeof(*session), GFP_KERNEL);
42         if (!session)
43                 return -ENOMEM;
44
45         session->target = target;
46         session->sid = info->sid;
47         BUILD_BUG_ON(sizeof(session->sess_param) !=
48                 sizeof(target->trgt_sess_param));
49         memcpy(&session->sess_param, &target->trgt_sess_param,
50                 sizeof(session->sess_param));
51         session->max_queued_cmnds = target->trgt_param.queued_cmnds;
52         atomic_set(&session->active_cmds, 0);
53
54         session->exp_cmd_sn = info->exp_cmd_sn;
55
56         session->initiator_name = kstrdup(info->initiator_name, GFP_KERNEL);
57         if (!session->initiator_name) {
58                 err = -ENOMEM;
59                 goto err;
60         }
61
62         name = kmalloc(strlen(info->user_name) + strlen(info->initiator_name) +
63                         1, GFP_KERNEL);
64         if (name == NULL) {
65                 err = -ENOMEM;
66                 goto err;
67         }
68
69         if (info->user_name[0] != '\0')
70                 sprintf(name, "%s@%s", info->user_name, info->initiator_name);
71         else
72                 sprintf(name, "%s", info->initiator_name);
73
74         INIT_LIST_HEAD(&session->conn_list);
75         INIT_LIST_HEAD(&session->pending_list);
76
77         spin_lock_init(&session->sn_lock);
78
79         spin_lock_init(&session->cmnd_hash_lock);
80         for (i = 0; i < ARRAY_SIZE(session->cmnd_hash); i++)
81                 INIT_LIST_HEAD(&session->cmnd_hash[i]);
82
83         session->next_ttt = 1;
84
85         session->scst_sess = scst_register_session(target->scst_tgt, 0,
86                 name, NULL, NULL);
87         if (session->scst_sess == NULL) {
88                 PRINT_ERROR("%s", "scst_register_session() failed");
89                 err = -ENOMEM;
90                 goto err;
91         }
92
93         kfree(name);
94
95         scst_sess_set_tgt_priv(session->scst_sess, session);
96         init_completion(&session->unreg_compl);
97
98         list_add(&session->session_list_entry, &target->session_list);
99
100         TRACE_MGMT_DBG("Session %p created: target %p, tid %u, sid %#Lx",
101                 session, target, target->tid, info->sid);
102
103         return 0;
104 err:
105         if (session) {
106                 kfree(session->initiator_name);
107                 kfree(session);
108                 kfree(name);
109         }
110         return err;
111 }
112
113 /* target_mutex supposed to be locked */
114 int session_add(struct iscsi_target *target, struct session_info *info)
115 {
116         struct iscsi_session *session;
117         int err = -EEXIST;
118
119         session = session_lookup(target, info->sid);
120         if (session) {
121                 PRINT_ERROR("Attempt to add session with existing SID %llx",
122                         info->sid);
123                 return err;
124         }
125
126         err = iscsi_session_alloc(target, info);
127
128         return err;
129 }
130
131 /* target_mutex supposed to be locked */
132 int session_free(struct iscsi_session *session)
133 {
134         unsigned int i;
135
136         TRACE_MGMT_DBG("Freeing session %p:%#Lx",
137                 session, (long long unsigned int)session->sid);
138
139         sBUG_ON(!list_empty(&session->conn_list));
140         if (unlikely(atomic_read(&session->active_cmds) != 0)) {
141                 PRINT_CRIT_ERROR("active_cmds not 0 (%d)!!",
142                         atomic_read(&session->active_cmds));
143                 sBUG();
144         }
145
146         for (i = 0; i < ARRAY_SIZE(session->cmnd_hash); i++)
147                 sBUG_ON(!list_empty(&session->cmnd_hash[i]));
148
149         if (session->scst_sess != NULL)
150                 scst_unregister_session(session->scst_sess, 1, NULL);
151
152         list_del(&session->session_list_entry);
153
154         kfree(session->initiator_name);
155         kfree(session);
156
157         return 0;
158 }
159
160 /* target_mutex supposed to be locked */
161 int session_del(struct iscsi_target *target, u64 sid)
162 {
163         struct iscsi_session *session;
164
165         session = session_lookup(target, sid);
166         if (!session)
167                 return -ENOENT;
168
169         if (!list_empty(&session->conn_list)) {
170                 PRINT_ERROR("%llu still have connections",
171                             (long long unsigned int)session->sid);
172                 return -EBUSY;
173         }
174
175         return session_free(session);
176 }
177
178 /* target_mutex supposed to be locked */
179 static void iscsi_session_info_show(struct seq_file *seq,
180                                     struct iscsi_target *target)
181 {
182         struct iscsi_session *session;
183
184         list_for_each_entry(session, &target->session_list,
185                             session_list_entry) {
186                 seq_printf(seq, "\tsid:%llx initiator:%s shutting down %d\n",
187                         (long long unsigned int)session->sid,
188                         session->initiator_name,
189                         session->shutting_down);
190                 conn_info_show(seq, session);
191         }
192         return;
193 }
194
195 static int iscsi_sessions_info_show(struct seq_file *seq, void *v)
196 {
197         return iscsi_info_show(seq, iscsi_session_info_show);
198 }
199
200 static int iscsi_session_seq_open(struct inode *inode, struct file *file)
201 {
202         return single_open(file, iscsi_sessions_info_show, NULL);
203 }
204
205 struct file_operations session_seq_fops = {
206         .owner          = THIS_MODULE,
207         .open           = iscsi_session_seq_open,
208         .read           = seq_read,
209         .llseek         = seq_lseek,
210         .release        = single_release,
211 };