24d2a3bf03698e926a3bf473895e2661f67398fa
[mirror/scst/.git] / iscsi-scst / kernel / config.c
1 /*
2  *  Copyright (C) 2004 - 2005 FUJITA Tomonori <tomof@acm.org>
3  *  Copyright (C) 2007 Vladislav Bolkhovitin
4  *  Copyright (C) 2007 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.
9  * 
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  */
15
16 #include <linux/proc_fs.h>
17
18 #include "iscsi.h"
19
20 #define ISCSI_PROC_VERSION_NAME         "version"
21
22 #if defined(DEBUG) || defined(TRACING)
23
24 #define ISCSI_PROC_LOG_ENTRY_NAME       "trace_level"
25
26 #include <linux/proc_fs.h>
27
28 static struct scst_proc_log iscsi_proc_local_trace_tbl[] =
29 {
30     { TRACE_D_READ,             "d_read" },
31     { TRACE_D_WRITE,            "d_write" },
32     { TRACE_CONN_OC,            "conn" },
33     { TRACE_D_IOV,              "iov" },
34     { TRACE_D_DUMP_PDU,         "pdu" },
35     { TRACE_NET_PG,             "net_page" },
36     { 0,                        NULL }
37 };
38
39 static int iscsi_log_info_show(struct seq_file *seq, void *v)
40 {
41         int res = 0;
42
43         TRACE_ENTRY();
44
45         res = scst_proc_log_entry_read(seq, trace_flag,
46                 iscsi_proc_local_trace_tbl);
47
48         TRACE_EXIT_RES(res);
49         return res;
50 }
51
52 static int iscsi_proc_log_entry_write(struct file *file, const char __user *buf,
53                                          size_t length, loff_t *off)
54 {
55         int res = 0;
56
57         TRACE_ENTRY();
58
59         res = scst_proc_log_entry_write(file, buf, length, &trace_flag,
60                 ISCSI_DEFAULT_LOG_FLAGS, iscsi_proc_local_trace_tbl);
61
62         TRACE_EXIT_RES(res);
63         return res;
64 }
65 #endif
66
67 static int iscsi_version_info_show(struct seq_file *seq, void *v)
68 {
69         TRACE_ENTRY();
70
71         seq_printf(seq, "%s\n", ISCSI_VERSION_STRING);
72
73 #ifdef EXTRACHECKS
74         seq_printf(seq, "EXTRACHECKS\n");
75 #endif
76
77 #ifdef TRACING
78         seq_printf(seq, "TRACING\n");
79 #endif
80
81 #ifdef DEBUG
82         seq_printf(seq, "DEBUG\n");
83 #endif
84
85 #ifdef DEBUG_DIGEST_FAILURES
86         seq_printf(seq, "DEBUG_DIGEST_FAILURES\n");
87 #endif
88
89         TRACE_EXIT();
90         return 0;
91 }
92
93 static struct scst_proc_data iscsi_version_proc_data = {
94         SCST_DEF_RW_SEQ_OP(NULL)
95         .show = iscsi_version_info_show,
96 };
97
98 #if defined(DEBUG) || defined(TRACING)
99 static struct scst_proc_data iscsi_log_proc_data = {
100         SCST_DEF_RW_SEQ_OP(iscsi_proc_log_entry_write)
101         .show = iscsi_log_info_show,
102 };
103 #endif
104
105 static __init int iscsi_proc_log_entry_build(struct scst_tgt_template *templ)
106 {
107         int res = 0;
108         struct proc_dir_entry *p, *root;
109
110         TRACE_ENTRY();
111
112         root = scst_proc_get_tgt_root(templ);
113         if (root) {
114                 p = scst_create_proc_entry(root, ISCSI_PROC_VERSION_NAME,
115                                          &iscsi_version_proc_data);
116                 if (p == NULL) {
117                         PRINT_ERROR("Not enough memory to register "
118                              "target driver %s entry %s in /proc",
119                               templ->name, ISCSI_PROC_VERSION_NAME);
120                         res = -ENOMEM;
121                         goto out;
122                 }
123
124 #if defined(DEBUG) || defined(TRACING)
125                 /* create the proc file entry for the device */
126                 iscsi_log_proc_data.data = (void *)templ->name;
127                 p = scst_create_proc_entry(root, ISCSI_PROC_LOG_ENTRY_NAME,
128                                         &iscsi_log_proc_data);
129                 if (p == NULL) {
130                         PRINT_ERROR("Not enough memory to register "
131                              "target driver %s entry %s in /proc",
132                               templ->name, ISCSI_PROC_LOG_ENTRY_NAME);
133                         res = -ENOMEM;
134                         goto out_remove_ver;
135                 }
136 #endif
137         }
138
139 out:
140         TRACE_EXIT_RES(res);
141         return res;
142
143 #if defined(DEBUG) || defined(TRACING)
144 out_remove_ver:
145         remove_proc_entry(ISCSI_PROC_VERSION_NAME, root);
146         goto out;
147 #endif
148 }
149
150 static void iscsi_proc_log_entry_clean(struct scst_tgt_template *templ)
151 {
152         struct proc_dir_entry *root;
153
154         TRACE_ENTRY();
155
156         root = scst_proc_get_tgt_root(templ);
157         if (root) {
158 #if defined(DEBUG) || defined(TRACING)
159                 remove_proc_entry(ISCSI_PROC_LOG_ENTRY_NAME, root);
160 #endif
161                 remove_proc_entry(ISCSI_PROC_VERSION_NAME, root);
162         }
163
164         TRACE_EXIT();
165         return;
166 }
167
168 struct proc_entries {
169         const char *name;
170         struct file_operations *fops;
171 };
172
173 static struct proc_entries iscsi_proc_entries[] =
174 {
175         {"session", &session_seq_fops},
176 };
177
178 static struct proc_dir_entry *proc_iscsi_dir;
179
180 void iscsi_procfs_exit(void)
181 {
182         int i;
183
184         if (!proc_iscsi_dir)
185                 return;
186
187         for (i = 0; i < ARRAY_SIZE(iscsi_proc_entries); i++)
188                 remove_proc_entry(iscsi_proc_entries[i].name, proc_iscsi_dir);
189
190         iscsi_proc_log_entry_clean(&iscsi_template);
191 }
192
193 int __init iscsi_procfs_init(void)
194 {
195         int i, err = 0;
196         struct proc_dir_entry *ent;
197
198         proc_iscsi_dir = scst_proc_get_tgt_root(&iscsi_template);
199         if (proc_iscsi_dir == NULL) {
200                 err = -ESRCH;
201                 goto out;
202         }
203
204         proc_iscsi_dir->owner = THIS_MODULE;
205
206         err = iscsi_proc_log_entry_build(&iscsi_template);
207         if (err < 0)
208                 goto out;
209
210         for (i = 0; i < ARRAY_SIZE(iscsi_proc_entries); i++) {
211                 ent = create_proc_entry(iscsi_proc_entries[i].name, 0, proc_iscsi_dir);
212                 if (ent)
213                         ent->proc_fops = iscsi_proc_entries[i].fops;
214                 else {
215                         err = -ENOMEM;
216                         goto err;
217                 }
218         }
219
220 out:
221         return err;
222
223 err:
224         if (proc_iscsi_dir)
225                 iscsi_procfs_exit();
226         goto out;
227 }
228
229 /* target_mutex supposed to be locked */
230 static int get_conn_info(struct iscsi_target *target, unsigned long ptr)
231 {
232         int err;
233         struct iscsi_session *session;
234         struct conn_info info;
235         struct iscsi_conn *conn;
236
237         if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
238                 return err;
239
240         session = session_lookup(target, info.sid);
241         if (!session)
242                 return -ENOENT;
243         conn = conn_lookup(session, info.cid);
244
245         info.cid = conn->cid;
246         info.stat_sn = conn->stat_sn;
247         info.exp_stat_sn = conn->exp_stat_sn;
248
249         if (copy_to_user((void *) ptr, &info, sizeof(info)))
250                 return -EFAULT;
251
252         return 0;
253 }
254
255 /* target_mutex supposed to be locked */
256 static int add_conn(struct iscsi_target *target, unsigned long ptr)
257 {
258         int err;
259         struct iscsi_session *session;
260         struct conn_info info;
261
262         if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
263                 return err;
264
265         if (!(session = session_lookup(target, info.sid)))
266                 return -ENOENT;
267
268         return conn_add(session, &info);
269 }
270
271 /* target_mutex supposed to be locked */
272 static int del_conn(struct iscsi_target *target, unsigned long ptr)
273 {
274         int err;
275         struct iscsi_session *session;
276         struct conn_info info;
277
278         if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
279                 return err;
280
281         if (!(session = session_lookup(target, info.sid)))
282                 return -ENOENT;
283
284         return conn_del(session, &info);
285 }
286
287 /* target_mutex supposed to be locked */
288 static int get_session_info(struct iscsi_target *target, unsigned long ptr)
289 {
290         int err;
291         struct iscsi_session *session;
292         struct session_info info;
293
294         if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
295                 return err;
296
297         session = session_lookup(target, info.sid);
298
299         if (!session)
300                 return -ENOENT;
301
302         info.exp_cmd_sn = session->exp_cmd_sn;
303         info.max_cmd_sn = session->max_cmd_sn;
304
305         if (copy_to_user((void *) ptr, &info, sizeof(info)))
306                 return -EFAULT;
307
308         return 0;
309 }
310
311 /* target_mutex supposed to be locked */
312 static int add_session(struct iscsi_target *target, unsigned long ptr)
313 {
314         int err;
315         struct session_info info;
316
317         if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
318                 return err;
319
320         info.initiator_name[ISCSI_NAME_LEN-1] = '\0';
321         info.user_name[ISCSI_NAME_LEN-1] = '\0';
322
323         return session_add(target, &info);
324 }
325
326 /* target_mutex supposed to be locked */
327 static int del_session(struct iscsi_target *target, unsigned long ptr)
328 {
329         int err;
330         struct session_info info;
331
332         if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
333                 return err;
334
335         return session_del(target, info.sid);
336 }
337
338 /* target_mutex supposed to be locked */
339 static int iscsi_param_config(struct iscsi_target *target, unsigned long ptr, int set)
340 {
341         int err;
342         struct iscsi_param_info info;
343
344         if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
345                 goto out;
346
347         if ((err = iscsi_param_set(target, &info, set)) < 0)
348                 goto out;
349
350         if (!set)
351                 err = copy_to_user((void *) ptr, &info, sizeof(info));
352
353 out:
354         return err;
355 }
356
357 /* target_mgmt_mutex supposed to be locked */
358 static int add_target(unsigned long ptr)
359 {
360         int err;
361         struct target_info info;
362
363         if ((err = copy_from_user(&info, (void *) ptr, sizeof(info))) < 0)
364                 return err;
365
366         if (!(err = target_add(&info)))
367                 err = copy_to_user((void *) ptr, &info, sizeof(info));
368
369         return err;
370 }
371
372 static long ioctl(struct file *file, unsigned int cmd, unsigned long arg)
373 {
374         struct iscsi_target *target = NULL;
375         long err;
376         u32 id;
377
378         if ((err = get_user(id, (u32 *) arg)) != 0)
379                 goto out;
380
381         if ((err = mutex_lock_interruptible(&target_mgmt_mutex)) < 0)
382                 goto out;
383
384         if (cmd == DEL_TARGET) {
385                 err = target_del(id);
386                 goto out_unlock;
387         }
388
389         target = target_lookup_by_id(id);
390
391         if (cmd == ADD_TARGET)
392                 if (target) {
393                         err = -EEXIST;
394                         PRINT_ERROR("Target %u already exist!", id);
395                         goto out_unlock;
396                 }
397
398         switch (cmd) {
399         case ADD_TARGET:
400                 err = add_target(arg);
401                 goto out_unlock;
402         }
403
404         if (!target) {
405                 PRINT_ERROR("can't find the target %u", id);
406                 err = -EINVAL;
407                 goto out_unlock;
408         }
409
410         mutex_lock(&target->target_mutex);
411
412         switch (cmd) {
413         case ADD_SESSION:
414                 err = add_session(target, arg);
415                 break;
416
417         case DEL_SESSION:
418                 err = del_session(target, arg);
419                 break;
420
421         case GET_SESSION_INFO:
422                 err = get_session_info(target, arg);
423                 break;
424
425         case ISCSI_PARAM_SET:
426                 err = iscsi_param_config(target, arg, 1);
427                 break;
428
429         case ISCSI_PARAM_GET:
430                 err = iscsi_param_config(target, arg, 0);
431                 break;
432
433         case ADD_CONN:
434                 err = add_conn(target, arg);
435                 break;
436
437         case DEL_CONN:
438                 err = del_conn(target, arg);
439                 break;
440
441         case GET_CONN_INFO:
442                 err = get_conn_info(target, arg);
443                 break;
444         
445         default:
446                 PRINT_ERROR("invalid ioctl cmd %x", cmd);
447                 err = -EINVAL;
448         }
449
450         mutex_unlock(&target->target_mutex);
451
452 out_unlock:
453         mutex_unlock(&target_mgmt_mutex);
454
455 out:
456         return err;
457 }
458
459 static int release(struct inode *inode, struct file *filp)
460 {
461         TRACE(TRACE_MGMT, "%s", "Releasing allocated resources");
462         target_del_all();
463         return 0;
464 }
465
466 struct file_operations ctr_fops = {
467         .owner          = THIS_MODULE,
468         .unlocked_ioctl = ioctl,
469         .compat_ioctl   = ioctl,
470         .release        = release,
471 };
472
473 #ifdef DEBUG
474 void iscsi_dump_iov(struct msghdr *msg)
475 {
476         if (trace_flag & TRACE_D_IOV) {
477                 int i;
478                 printk("%p, %d\n", msg->msg_iov, msg->msg_iovlen);
479                 for (i = 0; i < min_t(size_t, msg->msg_iovlen, 
480                                 ISCSI_CONN_IOV_MAX); i++) {
481                         printk("%d: %p,%d\n", i, msg->msg_iov[i].iov_base,
482                                 msg->msg_iov[i].iov_len);
483                 }
484         }
485 }
486
487 static void iscsi_dump_char(int ch)
488 {
489         static unsigned char text[16];
490         static int i = 0;
491
492         if (ch < 0) {
493                 while ((i % 16) != 0) {
494                         printk("   ");
495                         text[i] = ' ';
496                         i++;
497                         if ((i % 16) == 0)
498                                 printk(" | %.16s |\n", text);
499                         else if ((i % 4) == 0)
500                                 printk(" |");
501                 }
502                 i = 0;
503                 return;
504         }
505
506         text[i] = (ch < 0x20 || (ch >= 0x80 && ch <= 0xa0)) ? ' ' : ch;
507         printk(" %02x", ch);
508         i++;
509         if ((i % 16) == 0) {
510                 printk(" | %.16s |\n", text);
511                 i = 0;
512         } else if ((i % 4) == 0)
513                 printk(" |");
514 }
515
516 void iscsi_dump_pdu(struct iscsi_pdu *pdu)
517 {
518         if (trace_flag & TRACE_D_DUMP_PDU) {
519                 unsigned char *buf;
520                 int i;
521
522                 buf = (void *)&pdu->bhs;
523                 printk("BHS: (%p,%d)\n", buf, sizeof(pdu->bhs));
524                 for (i = 0; i < sizeof(pdu->bhs); i++)
525                         iscsi_dump_char(*buf++);
526                 iscsi_dump_char(-1);
527
528                 buf = (void *)pdu->ahs;
529                 printk("AHS: (%p,%d)\n", buf, pdu->ahssize);
530                 for (i = 0; i < pdu->ahssize; i++)
531                         iscsi_dump_char(*buf++);
532                 iscsi_dump_char(-1);
533
534                 printk("Data: (%d)\n", pdu->datasize);
535         }
536 }
537 #endif /* DEBUG */