9ad6f586d57c2251e8eb31e18fb2114ddfb41f60
[mirror/scst/.git] / iscsi-scst / kernel / target.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 <linux/delay.h>
18
19 #include "iscsi.h"
20 #include "digest.h"
21
22 #define MAX_NR_TARGETS  (1UL << 30)
23
24 DEFINE_MUTEX(target_mgmt_mutex);
25
26 /* All 3 protected by target_mgmt_mutex */
27 static LIST_HEAD(target_list);
28 static u32 next_target_id;
29 static u32 nr_targets;
30
31 static struct iscsi_sess_param default_session_param = {
32         .initial_r2t = 1,
33         .immediate_data = 1,
34         .max_connections = 1,
35         .max_recv_data_length = 8192,
36         .max_xmit_data_length = 8192,
37         .max_burst_length = 262144,
38         .first_burst_length = 65536,
39         .default_wait_time = 2,
40         .default_retain_time = 20,
41         .max_outstanding_r2t = 1,
42         .data_pdu_inorder = 1,
43         .data_sequence_inorder = 1,
44         .error_recovery_level = 0,
45         .header_digest = DIGEST_NONE,
46         .data_digest = DIGEST_NONE,
47         .ofmarker = 0,
48         .ifmarker = 0,
49         .ofmarkint = 2048,
50         .ifmarkint = 2048,
51 };
52
53 static struct iscsi_trgt_param default_target_param = {
54         .queued_cmnds = DEFAULT_NR_QUEUED_CMNDS,
55 };
56
57 /* target_mgmt_mutex supposed to be locked */
58 struct iscsi_target *target_lookup_by_id(u32 id)
59 {
60         struct iscsi_target *target;
61
62         list_for_each_entry(target, &target_list, target_list_entry) {
63                 if (target->tid == id)
64                         return target;
65         }
66         return NULL;
67 }
68
69 /* target_mgmt_mutex supposed to be locked */
70 static struct iscsi_target *target_lookup_by_name(char *name)
71 {
72         struct iscsi_target *target;
73
74         list_for_each_entry(target, &target_list, target_list_entry) {
75                 if (!strcmp(target->name, name))
76                         return target;
77         }
78         return NULL;
79 }
80
81 /* target_mgmt_mutex supposed to be locked */
82 static int iscsi_target_create(struct target_info *info, u32 tid)
83 {
84         int err = -EINVAL, len;
85         char *name = info->name;
86         struct iscsi_target *target;
87
88         TRACE_MGMT_DBG("Creating target tid %u, name %s", tid, name);
89
90         len = strlen(name);
91         if (!len) {
92                 PRINT_ERROR("The length of the target name is zero %u", tid);
93                 goto out;
94         }
95
96         if (!try_module_get(THIS_MODULE)) {
97                 PRINT_ERROR("Fail to get module %u", tid);
98                 goto out;
99         }
100
101         target = kzalloc(sizeof(*target), GFP_KERNEL);
102         if (!target) {
103                 err = -ENOMEM;
104                 goto out_put;
105         }
106
107         target->tid = info->tid = tid;
108
109         memcpy(&target->trgt_sess_param, &default_session_param,
110                 sizeof(default_session_param));
111         memcpy(&target->trgt_param, &default_target_param,
112                 sizeof(default_target_param));
113
114         strncpy(target->name, name, sizeof(target->name) - 1);
115
116         mutex_init(&target->target_mutex);
117         INIT_LIST_HEAD(&target->session_list);
118
119         target->scst_tgt = scst_register(&iscsi_template, target->name);
120         if (!target->scst_tgt) {
121                 PRINT_ERROR("%s", "scst_register() failed");
122                 err = -EBUSY;
123                 goto out_free;
124         }
125
126         list_add(&target->target_list_entry, &target_list);
127
128         return 0;
129
130 out_free:
131         kfree(target);
132
133 out_put:
134         module_put(THIS_MODULE);
135
136 out:
137         return err;
138 }
139
140 /* target_mgmt_mutex supposed to be locked */
141 int target_add(struct target_info *info)
142 {
143         int err = -EEXIST;
144         u32 tid = info->tid;
145
146         if (nr_targets > MAX_NR_TARGETS) {
147                 err = -EBUSY;
148                 goto out;
149         }
150
151         if (target_lookup_by_name(info->name))
152                 goto out;
153
154         if (tid && target_lookup_by_id(tid))
155                 goto out;
156
157         if (!tid) {
158                 do {
159                         if (!++next_target_id)
160                                 ++next_target_id;
161                 } while (target_lookup_by_id(next_target_id));
162
163                 tid = next_target_id;
164         }
165
166         err = iscsi_target_create(info, tid);
167         if (!err)
168                 nr_targets++;
169 out:
170         return err;
171 }
172
173 static void target_destroy(struct iscsi_target *target)
174 {
175         TRACE_MGMT_DBG("Destroying target tid %u", target->tid);
176
177         scst_unregister(target->scst_tgt);
178
179         kfree(target);
180
181         module_put(THIS_MODULE);
182 }
183
184 /* target_mgmt_mutex supposed to be locked */
185 int target_del(u32 id)
186 {
187         struct iscsi_target *target;
188         int err;
189
190         target = target_lookup_by_id(id);
191         if (!target) {
192                 err = -ENOENT;
193                 goto out;
194         }
195
196         mutex_lock(&target->target_mutex);
197
198         if (!list_empty(&target->session_list)) {
199                 err = -EBUSY;
200                 goto out_unlock;
201         }
202
203         list_del(&target->target_list_entry);
204         nr_targets--;
205
206         mutex_unlock(&target->target_mutex);
207
208         target_destroy(target);
209         return 0;
210
211 out_unlock:
212         mutex_unlock(&target->target_mutex);
213
214 out:
215         return err;
216 }
217
218 static void target_del_session(struct iscsi_target *target,
219         struct iscsi_session *session, bool deleting)
220 {
221         int flags = ISCSI_CONN_ACTIVE_CLOSE;
222
223         if (deleting)
224                 flags |= ISCSI_CONN_DELETING;
225
226         TRACE_MGMT_DBG("Cleaning up session %p", session);
227         if (!list_empty(&session->conn_list)) {
228                 struct iscsi_conn *conn, *tc;
229                 list_for_each_entry_safe(conn, tc, &session->conn_list,
230                                          conn_list_entry) {
231                         TRACE_MGMT_DBG("Mark conn %p closing", conn);
232                         __mark_conn_closed(conn, flags);
233                 }
234         } else {
235                 TRACE_MGMT_DBG("Freeing session %p without connections",
236                                session);
237                 session_del(target, session->sid);
238         }
239 }
240
241 /* target_mutex supposed to be locked */
242 void target_del_all_sess(struct iscsi_target *target, bool deleting)
243 {
244         struct iscsi_session *session, *ts;
245
246         TRACE_ENTRY();
247
248         if (!list_empty(&target->session_list)) {
249                 TRACE_MGMT_DBG("Deleting all sessions from target %p", target);
250                 list_for_each_entry_safe(session, ts, &target->session_list,
251                                                 session_list_entry) {
252                         target_del_session(target, session, deleting);
253                 }
254         }
255
256         TRACE_EXIT();
257         return;
258 }
259
260 void target_del_all(void)
261 {
262         struct iscsi_target *target, *t;
263
264         TRACE_ENTRY();
265
266         TRACE_MGMT_DBG("%s", "Deleting all targets");
267
268         /* Not the best, ToDo */
269         while (1) {
270                 mutex_lock(&target_mgmt_mutex);
271
272                 if (list_empty(&target_list))
273                         break;
274
275                 list_for_each_entry_safe(target, t, &target_list,
276                                          target_list_entry) {
277                         mutex_lock(&target->target_mutex);
278                         if (!list_empty(&target->session_list)) {
279                                 target_del_all_sess(target, true);
280                                 mutex_unlock(&target->target_mutex);
281                         } else {
282                                 TRACE_MGMT_DBG("Deleting target %p", target);
283                                 list_del(&target->target_list_entry);
284                                 nr_targets--;
285                                 mutex_unlock(&target->target_mutex);
286                                 target_destroy(target);
287                                 continue;
288                         }
289                 }
290                 mutex_unlock(&target_mgmt_mutex);
291                 msleep(100);
292         }
293
294         mutex_unlock(&target_mgmt_mutex);
295
296         TRACE_MGMT_DBG("%s", "Deleting all targets finished");
297
298         TRACE_EXIT();
299         return;
300 }
301
302 int iscsi_info_show(struct seq_file *seq, iscsi_show_info_t *func)
303 {
304         int err;
305         struct iscsi_target *target;
306
307         err = mutex_lock_interruptible(&target_mgmt_mutex);
308         if (err < 0)
309                 return err;
310
311         list_for_each_entry(target, &target_list, target_list_entry) {
312                 seq_printf(seq, "tid:%u name:%s\n", target->tid, target->name);
313
314                 mutex_lock(&target->target_mutex);
315                 func(seq, target);
316                 mutex_unlock(&target->target_mutex);
317         }
318
319         mutex_unlock(&target_mgmt_mutex);
320
321         return 0;
322 }