6a3de7039cc8f33d58a7b8d49f51344af579def5
[mirror/scst/.git] / iscsi-scst / kernel / config.c
1 /*
2  *  Copyright (C) 2004 - 2005 FUJITA Tomonori <tomof@acm.org>
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.
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 "iscsi.h"
17
18 /* Protected by target_mgmt_mutex */
19 int ctr_open_state;
20
21 #ifdef CONFIG_SCST_PROC
22
23 #include <linux/proc_fs.h>
24
25 #define ISCSI_PROC_VERSION_NAME         "version"
26
27 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
28
29 #define ISCSI_PROC_LOG_ENTRY_NAME       "trace_level"
30
31 static struct scst_trace_log iscsi_local_trace_tbl[] = {
32     { TRACE_D_WRITE,            "d_write" },
33     { TRACE_CONN_OC,            "conn" },
34     { TRACE_CONN_OC_DBG,        "conn_dbg" },
35     { TRACE_D_IOV,              "iov" },
36     { TRACE_D_DUMP_PDU,         "pdu" },
37     { TRACE_NET_PG,             "net_page" },
38     { 0,                        NULL }
39 };
40
41 static int iscsi_log_info_show(struct seq_file *seq, void *v)
42 {
43         int res = 0;
44
45         TRACE_ENTRY();
46
47         res = scst_proc_log_entry_read(seq, trace_flag,
48                 iscsi_local_trace_tbl);
49
50         TRACE_EXIT_RES(res);
51         return res;
52 }
53
54 static ssize_t iscsi_proc_log_entry_write(struct file *file,
55         const char __user *buf, size_t length, loff_t *off)
56 {
57         int res = 0;
58
59         TRACE_ENTRY();
60
61         res = scst_proc_log_entry_write(file, buf, length, &trace_flag,
62                 ISCSI_DEFAULT_LOG_FLAGS, iscsi_local_trace_tbl);
63
64         TRACE_EXIT_RES(res);
65         return res;
66 }
67
68 #endif /* DEBUG or TRACE */
69
70 static int iscsi_version_info_show(struct seq_file *seq, void *v)
71 {
72         TRACE_ENTRY();
73
74         seq_printf(seq, "%s\n", ISCSI_VERSION_STRING);
75
76 #ifdef CONFIG_SCST_EXTRACHECKS
77         seq_printf(seq, "EXTRACHECKS\n");
78 #endif
79
80 #ifdef CONFIG_SCST_TRACING
81         seq_printf(seq, "TRACING\n");
82 #endif
83
84 #ifdef CONFIG_SCST_DEBUG
85         seq_printf(seq, "DEBUG\n");
86 #endif
87
88 #ifdef CONFIG_SCST_ISCSI_DEBUG_DIGEST_FAILURES
89         seq_printf(seq, "DEBUG_DIGEST_FAILURES\n");
90 #endif
91
92         TRACE_EXIT();
93         return 0;
94 }
95
96 static struct scst_proc_data iscsi_version_proc_data = {
97         SCST_DEF_RW_SEQ_OP(NULL)
98         .show = iscsi_version_info_show,
99 };
100
101 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
102 static struct scst_proc_data iscsi_log_proc_data = {
103         SCST_DEF_RW_SEQ_OP(iscsi_proc_log_entry_write)
104         .show = iscsi_log_info_show,
105 };
106 #endif
107
108 static __init int iscsi_proc_log_entry_build(struct scst_tgt_template *templ)
109 {
110         int res = 0;
111         struct proc_dir_entry *p, *root;
112
113         TRACE_ENTRY();
114
115         root = scst_proc_get_tgt_root(templ);
116         if (root) {
117                 p = scst_create_proc_entry(root, ISCSI_PROC_VERSION_NAME,
118                                            &iscsi_version_proc_data);
119                 if (p == NULL) {
120                         PRINT_ERROR("Not enough memory to register "
121                              "target driver %s entry %s in /proc",
122                               templ->name, ISCSI_PROC_VERSION_NAME);
123                         res = -ENOMEM;
124                         goto out;
125                 }
126
127 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
128                 /* create the proc file entry for the device */
129                 iscsi_log_proc_data.data = (void *)templ->name;
130                 p = scst_create_proc_entry(root, ISCSI_PROC_LOG_ENTRY_NAME,
131                                            &iscsi_log_proc_data);
132                 if (p == NULL) {
133                         PRINT_ERROR("Not enough memory to register "
134                              "target driver %s entry %s in /proc",
135                               templ->name, ISCSI_PROC_LOG_ENTRY_NAME);
136                         res = -ENOMEM;
137                         goto out_remove_ver;
138                 }
139 #endif
140         }
141
142 out:
143         TRACE_EXIT_RES(res);
144         return res;
145
146 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
147 out_remove_ver:
148         remove_proc_entry(ISCSI_PROC_VERSION_NAME, root);
149         goto out;
150 #endif
151 }
152
153 static void iscsi_proc_log_entry_clean(struct scst_tgt_template *templ)
154 {
155         struct proc_dir_entry *root;
156
157         TRACE_ENTRY();
158
159         root = scst_proc_get_tgt_root(templ);
160         if (root) {
161 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
162                 remove_proc_entry(ISCSI_PROC_LOG_ENTRY_NAME, root);
163 #endif
164                 remove_proc_entry(ISCSI_PROC_VERSION_NAME, root);
165         }
166
167         TRACE_EXIT();
168         return;
169 }
170
171 struct proc_entries {
172         const char *name;
173         const struct file_operations *const fops;
174 };
175
176 static struct proc_entries iscsi_proc_entries[] = {
177         {"session", &session_seq_fops},
178 };
179
180 static struct proc_dir_entry *proc_iscsi_dir;
181
182 void iscsi_procfs_exit(void)
183 {
184         unsigned int i;
185
186         if (!proc_iscsi_dir)
187                 return;
188
189         for (i = 0; i < ARRAY_SIZE(iscsi_proc_entries); i++)
190                 remove_proc_entry(iscsi_proc_entries[i].name, proc_iscsi_dir);
191
192         iscsi_proc_log_entry_clean(&iscsi_template);
193 }
194
195 int __init iscsi_procfs_init(void)
196 {
197         unsigned int i;
198         int err = 0;
199         struct proc_dir_entry *ent;
200
201         proc_iscsi_dir = scst_proc_get_tgt_root(&iscsi_template);
202         if (proc_iscsi_dir == NULL) {
203                 err = -ESRCH;
204                 goto out;
205         }
206
207 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
208         proc_iscsi_dir->owner = THIS_MODULE;
209 #endif
210
211         err = iscsi_proc_log_entry_build(&iscsi_template);
212         if (err < 0)
213                 goto out;
214
215         for (i = 0; i < ARRAY_SIZE(iscsi_proc_entries); i++) {
216                 ent = create_proc_entry(iscsi_proc_entries[i].name, 0,
217                                         proc_iscsi_dir);
218                 if (ent)
219                         ent->proc_fops = iscsi_proc_entries[i].fops;
220                 else {
221                         err = -ENOMEM;
222                         goto err;
223                 }
224         }
225
226 out:
227         return err;
228
229 err:
230         if (proc_iscsi_dir)
231                 iscsi_procfs_exit();
232         goto out;
233 }
234
235 #else /* CONFIG_SCST_PROC */
236
237 /* Protected by target_mgmt_mutex */
238 static LIST_HEAD(iscsi_attrs_list);
239
240 static ssize_t iscsi_version_show(struct kobject *kobj,
241         struct kobj_attribute *attr, char *buf)
242 {
243         TRACE_ENTRY();
244
245         sprintf(buf, "%s\n", ISCSI_VERSION_STRING);
246
247 #ifdef CONFIG_SCST_EXTRACHECKS
248         strcat(buf, "EXTRACHECKS\n");
249 #endif
250
251 #ifdef CONFIG_SCST_TRACING
252         strcat(buf, "TRACING\n");
253 #endif
254
255 #ifdef CONFIG_SCST_DEBUG
256         strcat(buf, "DEBUG\n");
257 #endif
258
259 #ifdef CONFIG_SCST_ISCSI_DEBUG_DIGEST_FAILURES
260         strcat(buf, "DEBUG_DIGEST_FAILURES\n");
261 #endif
262
263         TRACE_EXIT();
264         return strlen(buf);
265 }
266
267 static struct kobj_attribute iscsi_version_attr =
268         __ATTR(version, S_IRUGO, iscsi_version_show, NULL);
269
270 static ssize_t iscsi_open_state_show(struct kobject *kobj,
271         struct kobj_attribute *attr, char *buf)
272 {
273         switch (ctr_open_state) {
274         case ISCSI_CTR_OPEN_STATE_CLOSED:
275                 sprintf(buf, "%s\n", "closed");
276                 break;
277         case ISCSI_CTR_OPEN_STATE_OPEN:
278                 sprintf(buf, "%s\n", "open");
279                 break;
280         case ISCSI_CTR_OPEN_STATE_CLOSING:
281                 sprintf(buf, "%s\n", "closing");
282                 break;
283         default:
284                 sprintf(buf, "%s\n", "unknown");
285                 break;
286         }
287
288         return strlen(buf);
289 }
290
291 static struct kobj_attribute iscsi_open_state_attr =
292         __ATTR(open_state, S_IRUGO, iscsi_open_state_show, NULL);
293
294 const struct attribute *iscsi_attrs[] = {
295         &iscsi_version_attr.attr,
296         &iscsi_open_state_attr.attr,
297         NULL,
298 };
299
300 #endif /* CONFIG_SCST_PROC */
301
302 /* target_mgmt_mutex supposed to be locked */
303 static int add_conn(void __user *ptr)
304 {
305         int err;
306         struct iscsi_session *session;
307         struct iscsi_kern_conn_info info;
308         struct iscsi_target *target;
309
310         TRACE_ENTRY();
311
312         err = copy_from_user(&info, ptr, sizeof(info));
313         if (err < 0)
314                 goto out;
315
316         target = target_lookup_by_id(info.tid);
317         if (target == NULL) {
318                 PRINT_ERROR("Target %d not found", info.tid);
319                 err = -ENOENT;
320                 goto out;
321         }
322
323         mutex_lock(&target->target_mutex);
324
325         session = session_lookup(target, info.sid);
326         if (!session) {
327                 PRINT_ERROR("Session %lld not found",
328                         (long long unsigned int)info.tid);
329                 err = -ENOENT;
330                 goto out_unlock;
331         }
332
333         err = __add_conn(session, &info);
334
335 out_unlock:
336         mutex_unlock(&target->target_mutex);
337
338 out:
339         TRACE_EXIT_RES(err);
340         return err;
341 }
342
343 /* target_mgmt_mutex supposed to be locked */
344 static int del_conn(void __user *ptr)
345 {
346         int err;
347         struct iscsi_session *session;
348         struct iscsi_kern_conn_info info;
349         struct iscsi_target *target;
350
351         TRACE_ENTRY();
352
353         err = copy_from_user(&info, ptr, sizeof(info));
354         if (err < 0)
355                 goto out;
356
357         target = target_lookup_by_id(info.tid);
358         if (target == NULL) {
359                 PRINT_ERROR("Target %d not found", info.tid);
360                 err = -ENOENT;
361                 goto out;
362         }
363
364         mutex_lock(&target->target_mutex);
365
366         session = session_lookup(target, info.sid);
367         if (!session) {
368                 PRINT_ERROR("Session %llx not found",
369                         (long long unsigned int)info.sid);
370                 err = -ENOENT;
371                 goto out_unlock;
372         }
373
374         err = __del_conn(session, &info);
375
376 out_unlock:
377         mutex_unlock(&target->target_mutex);
378
379 out:
380         TRACE_EXIT_RES(err);
381         return err;
382 }
383
384 /* target_mgmt_mutex supposed to be locked */
385 static int add_session(void __user *ptr)
386 {
387         int err;
388         struct iscsi_kern_session_info *info;
389         struct iscsi_target *target;
390
391         TRACE_ENTRY();
392
393         info = kzalloc(sizeof(*info), GFP_KERNEL);
394         if (info == NULL) {
395                 PRINT_ERROR("Can't alloc info (size %d)", sizeof(*info));
396                 err = -ENOMEM;
397                 goto out;
398         }
399
400         err = copy_from_user(info, ptr, sizeof(*info));
401         if (err != 0) {
402                 PRINT_ERROR("copy_from_user() didn't copy %d bytes", err);
403                 err = -EFAULT;
404                 goto out_free;
405         }
406
407         info->initiator_name[sizeof(info->initiator_name)-1] = '\0';
408 #ifdef CONFIG_SCST_PROC
409         info->user_name[sizeof(info->user_name)-1] = '\0';
410 #endif
411
412         target = target_lookup_by_id(info->tid);
413         if (target == NULL) {
414                 PRINT_ERROR("Target %d not found", info->tid);
415                 err = -ENOENT;
416                 goto out_free;
417         }
418
419         err = __add_session(target, info);
420
421 out_free:
422         kfree(info);
423
424 out:
425         TRACE_EXIT_RES(err);
426         return err;
427 }
428
429 /* target_mgmt_mutex supposed to be locked */
430 static int del_session(void __user *ptr)
431 {
432         int err;
433         struct iscsi_kern_session_info *info;
434         struct iscsi_target *target;
435
436         TRACE_ENTRY();
437
438         info = kzalloc(sizeof(*info), GFP_KERNEL);
439         if (info == NULL) {
440                 PRINT_ERROR("Can't alloc info (size %d)", sizeof(*info));
441                 err = -ENOMEM;
442                 goto out;
443         }
444
445         err = copy_from_user(info, ptr, sizeof(*info));
446         if (err != 0) {
447                 PRINT_ERROR("copy_from_user() didn't copy %d bytes", err);
448                 err = -EFAULT;
449                 goto out_free;
450         }
451
452         info->initiator_name[sizeof(info->initiator_name)-1] = '\0';
453 #ifdef CONFIG_SCST_PROC
454         info->user_name[sizeof(info->user_name)-1] = '\0';
455 #endif
456
457         target = target_lookup_by_id(info->tid);
458         if (target == NULL) {
459                 PRINT_ERROR("Target %d not found", info->tid);
460                 err = -ENOENT;
461                 goto out_free;
462         }
463
464         mutex_lock(&target->target_mutex);
465         err = __del_session(target, info->sid);
466         mutex_unlock(&target->target_mutex);
467
468 out_free:
469         kfree(info);
470
471 out:
472         TRACE_EXIT_RES(err);
473         return err;
474 }
475
476 /* target_mgmt_mutex supposed to be locked */
477 static int iscsi_params_config(void __user *ptr, int set)
478 {
479         int err;
480         struct iscsi_kern_params_info info;
481         struct iscsi_target *target;
482
483         TRACE_ENTRY();
484
485         err = copy_from_user(&info, ptr, sizeof(info));
486         if (err < 0)
487                 goto out;
488
489         target = target_lookup_by_id(info.tid);
490         if (target == NULL) {
491                 PRINT_ERROR("Target %d not found", info.tid);
492                 err = -ENOENT;
493                 goto out;
494         }
495
496         mutex_lock(&target->target_mutex);
497         err = iscsi_params_set(target, &info, set);
498         mutex_unlock(&target->target_mutex);
499
500         if (err < 0)
501                 goto out;
502
503         if (!set)
504                 err = copy_to_user(ptr, &info, sizeof(info));
505
506 out:
507         TRACE_EXIT_RES(err);
508         return err;
509 }
510
511 #ifndef CONFIG_SCST_PROC
512
513 /* target_mgmt_mutex supposed to be locked */
514 static int mgmt_cmd_callback(void __user *ptr)
515 {
516         int err = 0, rc;
517         struct iscsi_kern_mgmt_cmd_res_info cinfo;
518         struct scst_sysfs_user_info *info;
519
520         TRACE_ENTRY();
521
522         rc = copy_from_user(&cinfo, ptr, sizeof(cinfo));
523         if (rc != 0) {
524                 err = -EFAULT;
525                 goto out;
526         }
527
528         cinfo.value[sizeof(cinfo.value)-1] = '\0';
529
530         info = scst_sysfs_user_get_info(cinfo.cookie);
531         TRACE_DBG("cookie %u, info %p, result %d", cinfo.cookie, info,
532                 cinfo.result);
533         if (info == NULL) {
534                 err = -EINVAL;
535                 goto out;
536         }
537
538         info->info_status = 0;
539
540         if (cinfo.result != 0) {
541                 info->info_status = cinfo.result;
542                 goto out_complete;
543         }
544
545         switch (cinfo.req_cmd) {
546         case E_ENABLE_TARGET:
547         case E_DISABLE_TARGET:
548         {
549                 struct iscsi_target *target;
550
551                 target = target_lookup_by_id(cinfo.tid);
552                 if (target == NULL) {
553                         PRINT_ERROR("Target %d not found", cinfo.tid);
554                         err = -ENOENT;
555                         goto out_status;
556                 }
557
558                 target->tgt_enabled = (cinfo.req_cmd == E_ENABLE_TARGET) ? 1 : 0;
559                 break;
560         }
561
562         case E_GET_ATTR_VALUE:
563                 info->data = kstrdup(cinfo.value, GFP_KERNEL);
564                 if (info->data == NULL) {
565                         PRINT_ERROR("Can't dublicate value %s", cinfo.value);
566                         info->info_status = -ENOMEM;
567                         goto out_complete;
568                 }
569                 break;
570         }
571
572 out_complete:
573         complete(&info->info_completion);
574
575 out:
576         TRACE_EXIT_RES(err);
577         return err;
578
579 out_status:
580         info->info_status = err;
581         goto out_complete;
582 }
583
584 static ssize_t iscsi_attr_show(struct kobject *kobj,
585         struct kobj_attribute *attr, char *buf)
586 {
587         int pos;
588         struct iscsi_attr *tgt_attr;
589         void *value;
590
591         TRACE_ENTRY();
592
593         tgt_attr = container_of(attr, struct iscsi_attr, attr);
594
595         pos = iscsi_sysfs_send_event(
596                 (tgt_attr->target != NULL) ? tgt_attr->target->tid : 0,
597                 E_GET_ATTR_VALUE, tgt_attr->name, NULL, &value);
598
599         if (pos != 0)
600                 goto out;
601
602         pos = scnprintf(buf, SCST_SYSFS_BLOCK_SIZE, "%s\n", (char *)value);
603
604         kfree(value);
605
606 out:
607         TRACE_EXIT_RES(pos);
608         return pos;
609 }
610
611 static ssize_t iscsi_attr_store(struct kobject *kobj,
612         struct kobj_attribute *attr, const char *buf, size_t count)
613 {
614         int res;
615         char *buffer;
616         struct iscsi_attr *tgt_attr;
617
618         TRACE_ENTRY();
619
620         buffer = kzalloc(count+1, GFP_KERNEL);
621         if (buffer == NULL) {
622                 res = -ENOMEM;
623                 goto out;
624         }
625
626         memcpy(buffer, buf, count);
627         buffer[count] = '\0';
628
629         tgt_attr = container_of(attr, struct iscsi_attr, attr);
630
631         res = iscsi_sysfs_send_event(
632                 (tgt_attr->target != NULL) ? tgt_attr->target->tid : 0,
633                 E_SET_ATTR_VALUE, tgt_attr->name, buffer, NULL);
634
635         kfree(buffer);
636
637         if (res == 0)
638                 res = count;
639
640 out:
641         TRACE_EXIT_RES(res);
642         return res;
643 }
644
645 /*
646  * target_mgmt_mutex supposed to be locked. If target != 0, target_mutex
647  * supposed to be locked as well.
648  */
649 int iscsi_add_attr(struct iscsi_target *target,
650         const struct iscsi_kern_attr *attr_info)
651 {
652         int res = 0;
653         struct iscsi_attr *tgt_attr;
654         struct list_head *attrs_list;
655         const char *name;
656
657         TRACE_ENTRY();
658
659         if (target != NULL) {
660                 attrs_list = &target->attrs_list;
661                 name = target->name;
662         } else {
663                 attrs_list = &iscsi_attrs_list;
664                 name = "global";
665         }
666
667         list_for_each_entry(tgt_attr, attrs_list, attrs_list_entry) {
668                 if (strncmp(tgt_attr->name, attr_info->name,
669                                 sizeof(tgt_attr->name) == 0)) {
670                         PRINT_ERROR("Attribute %s for %s already exist",
671                                 attr_info->name, name);
672                         res = -EEXIST;
673                         goto out;
674                 }
675         }
676
677         TRACE_DBG("Adding %s's attr %s with mode %x", name,
678                 attr_info->name, attr_info->mode);
679
680         tgt_attr = kzalloc(sizeof(*tgt_attr), GFP_KERNEL);
681         if (tgt_attr == NULL) {
682                 PRINT_ERROR("Unable to allocate user (size %d)",
683                         sizeof(*tgt_attr));
684                 res = -ENOMEM;
685                 goto out;
686         }
687
688         tgt_attr->target = target;
689
690         tgt_attr->name = kstrdup(attr_info->name, GFP_KERNEL);
691         if (tgt_attr->name == NULL) {
692                 PRINT_ERROR("Unable to allocate attr %s name/value (target %s)",
693                         attr_info->name, name);
694                 res = -ENOMEM;
695                 goto out_free;
696         }
697
698         list_add(&tgt_attr->attrs_list_entry, attrs_list);
699
700         tgt_attr->attr.attr.name = tgt_attr->name;
701         tgt_attr->attr.attr.owner = THIS_MODULE;
702         tgt_attr->attr.attr.mode = attr_info->mode & (S_IRUGO | S_IWUGO);
703         tgt_attr->attr.show = iscsi_attr_show;
704         tgt_attr->attr.store = iscsi_attr_store;
705
706         res = sysfs_create_file(
707                 (target != NULL) ? scst_sysfs_get_tgt_kobj(target->scst_tgt) :
708                                 scst_sysfs_get_tgtt_kobj(&iscsi_template),
709                 &tgt_attr->attr.attr);
710         if (res != 0) {
711                 PRINT_ERROR("Unable to create file '%s' for target '%s'",
712                         tgt_attr->attr.attr.name, name);
713                 goto out_del;
714         }
715
716 out:
717         TRACE_EXIT_RES(res);
718         return res;
719
720 out_del:
721         list_del(&tgt_attr->attrs_list_entry);
722
723 out_free:
724         kfree(tgt_attr->name);
725         kfree(tgt_attr);
726         goto out;
727 }
728
729 void __iscsi_del_attr(struct iscsi_target *target,
730         struct iscsi_attr *tgt_attr)
731 {
732         TRACE_ENTRY();
733
734         TRACE_DBG("Deleting %s's attr %s",
735                 (target != NULL) ? target->name : "global", tgt_attr->name);
736
737         list_del(&tgt_attr->attrs_list_entry);
738
739         sysfs_remove_file((target != NULL) ?
740                         scst_sysfs_get_tgt_kobj(target->scst_tgt) :
741                         scst_sysfs_get_tgtt_kobj(&iscsi_template),
742                 &tgt_attr->attr.attr);
743
744         kfree(tgt_attr->name);
745         kfree(tgt_attr);
746
747         TRACE_EXIT();
748         return;
749 }
750
751 /*
752  * target_mgmt_mutex supposed to be locked. If target != 0, target_mutex
753  * supposed to be locked as well.
754  */
755 static int iscsi_del_attr(struct iscsi_target *target,
756         const char *attr_name)
757 {
758         int res = 0;
759         struct iscsi_attr *tgt_attr, *a;
760         struct list_head *attrs_list;
761
762         TRACE_ENTRY();
763
764         if (target != NULL)
765                 attrs_list = &target->attrs_list;
766         else
767                 attrs_list = &iscsi_attrs_list;
768
769         tgt_attr = NULL;
770         list_for_each_entry(a, attrs_list, attrs_list_entry) {
771                 if (strncmp(a->name, attr_name, sizeof(a->name)) == 0) {
772                         tgt_attr = a;
773                         break;
774                 }
775         }
776
777         if (tgt_attr == NULL) {
778                 PRINT_ERROR("attr %s not found (target %s)", attr_name,
779                         (target != NULL) ? target->name : "global");
780                 res = -ENOENT;
781                 goto out;
782         }
783
784         __iscsi_del_attr(target, tgt_attr);
785
786 out:
787         TRACE_EXIT_RES(res);
788         return res;
789 }
790
791 /* target_mgmt_mutex supposed to be locked */
792 static int iscsi_attr_cmd(void __user *ptr, unsigned int cmd)
793 {
794         int rc, err = 0;
795         struct iscsi_kern_attr_info info;
796         struct iscsi_target *target;
797         struct scst_sysfs_user_info *i = NULL;
798
799         TRACE_ENTRY();
800
801         rc = copy_from_user(&info, ptr, sizeof(info));
802         if (rc != 0) {
803                 err = -EFAULT;
804                 goto out;
805         }
806
807         info.attr.name[sizeof(info.attr.name)-1] = '\0';
808
809         if (info.cookie != 0) {
810                 i = scst_sysfs_user_get_info(info.cookie);
811                 TRACE_DBG("cookie %u, uinfo %p", info.cookie, i);
812                 if (i == NULL) {
813                         err = -EINVAL;
814                         goto out;
815                 }
816         }
817
818         target = target_lookup_by_id(info.tid);
819
820         if (target != NULL)
821                 mutex_lock(&target->target_mutex);
822
823         switch (cmd) {
824         case ISCSI_ATTR_ADD:
825                 err = iscsi_add_attr(target, &info.attr);
826                 break;
827         case ISCSI_ATTR_DEL:
828                 err = iscsi_del_attr(target, info.attr.name);
829                 break;
830         default:
831                 sBUG();
832         }
833
834         if (target != NULL)
835                 mutex_unlock(&target->target_mutex);
836
837         if (i != NULL) {
838                 i->info_status = err;
839                 complete(&i->info_completion);
840         }
841
842 out:
843         TRACE_EXIT_RES(err);
844         return err;
845 }
846
847 #endif /* CONFIG_SCST_PROC */
848
849 /* target_mgmt_mutex supposed to be locked */
850 static int add_target(void __user *ptr)
851 {
852         int err;
853         struct iscsi_kern_target_info *info;
854 #ifndef CONFIG_SCST_PROC
855         struct scst_sysfs_user_info *uinfo;
856 #endif
857
858         TRACE_ENTRY();
859
860         info = kzalloc(sizeof(*info), GFP_KERNEL);
861         if (info == NULL) {
862                 PRINT_ERROR("Can't alloc info (size %d)", sizeof(*info));
863                 err = -ENOMEM;
864                 goto out;
865         }
866
867         err = copy_from_user(info, ptr, sizeof(*info));
868         if (err != 0) {
869                 PRINT_ERROR("copy_from_user() didn't copy %d bytes", err);
870                 err = -EFAULT;
871                 goto out_free;
872         }
873
874         if (target_lookup_by_id(info->tid) != NULL) {
875                 PRINT_ERROR("Target %u already exist!", info->tid);
876                 err = -EEXIST;
877                 goto out_free;
878         }
879
880         info->name[sizeof(info->name)-1] = '\0';
881
882 #ifndef CONFIG_SCST_PROC
883         if (info->cookie != 0) {
884                 uinfo = scst_sysfs_user_get_info(info->cookie);
885                 TRACE_DBG("cookie %u, uinfo %p", info->cookie, uinfo);
886                 if (uinfo == NULL) {
887                         err = -EINVAL;
888                         goto out_free;
889                 }
890         } else
891                 uinfo = NULL;
892 #endif
893
894         err = __add_target(info);
895
896 #ifndef CONFIG_SCST_PROC
897         if (uinfo != NULL) {
898                 uinfo->info_status = err;
899                 complete(&uinfo->info_completion);
900         }
901 #endif
902
903 out_free:
904         kfree(info);
905
906 out:
907         TRACE_EXIT_RES(err);
908         return err;
909 }
910
911 /* target_mgmt_mutex supposed to be locked */
912 static int del_target(void __user *ptr)
913 {
914         int err;
915         struct iscsi_kern_target_info info;
916 #ifndef CONFIG_SCST_PROC
917         struct scst_sysfs_user_info *uinfo;
918 #endif
919
920         TRACE_ENTRY();
921
922         err = copy_from_user(&info, ptr, sizeof(info));
923         if (err < 0)
924                 goto out;
925
926         info.name[sizeof(info.name)-1] = '\0';
927
928 #ifndef CONFIG_SCST_PROC
929         if (info.cookie != 0) {
930                 uinfo = scst_sysfs_user_get_info(info.cookie);
931                 TRACE_DBG("cookie %u, uinfo %p", info.cookie, uinfo);
932                 if (uinfo == NULL) {
933                         err = -EINVAL;
934                         goto out;
935                 }
936         } else
937                 uinfo = NULL;
938 #endif
939
940         err = __del_target(info.tid);
941
942 #ifndef CONFIG_SCST_PROC
943         if (uinfo != NULL) {
944                 uinfo->info_status = err;
945                 complete(&uinfo->info_completion);
946         }
947 #endif
948
949 out:
950         TRACE_EXIT_RES(err);
951         return err;
952 }
953
954 static int iscsi_register(void __user *arg)
955 {
956         struct iscsi_kern_register_info reg;
957         char ver[sizeof(ISCSI_SCST_INTERFACE_VERSION)+1];
958         int res, rc;
959
960         TRACE_ENTRY();
961
962         rc = copy_from_user(&reg, arg, sizeof(reg));
963         if (rc != 0) {
964                 PRINT_ERROR("%s", "Unable to get register info");
965                 res = -EFAULT;
966                 goto out;
967         }
968
969         rc = copy_from_user(ver, (void __user *)(unsigned long)reg.version,
970                                 sizeof(ver));
971         if (rc < 0) {
972                 PRINT_ERROR("%s", "Unable to get version string");
973                 res = -EFAULT;
974                 goto out;
975         }
976         ver[sizeof(ver)-1] = '\0';
977
978         if (strcmp(ver, ISCSI_SCST_INTERFACE_VERSION) != 0) {
979                 PRINT_ERROR("Incorrect version of user space %s (expected %s)",
980                         ver, ISCSI_SCST_INTERFACE_VERSION);
981                 res = -EINVAL;
982                 goto out;
983         }
984
985         memset(&reg, 0, sizeof(reg));
986         reg.max_data_seg_len = ISCSI_CONN_IOV_MAX << PAGE_SHIFT;
987
988         res = 0;
989
990         rc = copy_to_user(arg, &reg, sizeof(reg));
991         if (rc != 0) {
992                 PRINT_ERROR("copy_to_user() failed to copy %d bytes", res);
993                 res = -EFAULT;
994                 goto out;
995         }
996
997 out:
998         TRACE_EXIT_RES(res);
999         return res;
1000 }
1001
1002 static long ioctl(struct file *file, unsigned int cmd, unsigned long arg)
1003 {
1004         long err;
1005
1006         TRACE_ENTRY();
1007
1008         if (cmd == REGISTER_USERD) {
1009                 err = iscsi_register((void __user *)arg);
1010                 goto out;
1011         }
1012
1013         err = mutex_lock_interruptible(&target_mgmt_mutex);
1014         if (err < 0)
1015                 goto out;
1016
1017         switch (cmd) {
1018         case ADD_TARGET:
1019                 err = add_target((void __user *)arg);
1020                 break;
1021
1022         case DEL_TARGET:
1023                 err = del_target((void __user *)arg);
1024                 break;
1025
1026 #ifndef CONFIG_SCST_PROC
1027         case ISCSI_ATTR_ADD:
1028         case ISCSI_ATTR_DEL:
1029                 err = iscsi_attr_cmd((void __user *)arg, cmd);
1030                 break;
1031
1032         case MGMT_CMD_CALLBACK:
1033                 err = mgmt_cmd_callback((void __user *)arg);
1034                 break;
1035 #endif
1036
1037         case ADD_SESSION:
1038                 err = add_session((void __user *)arg);
1039                 break;
1040
1041         case DEL_SESSION:
1042                 err = del_session((void __user *)arg);
1043                 break;
1044
1045         case ISCSI_PARAM_SET:
1046                 err = iscsi_params_config((void __user *)arg, 1);
1047                 break;
1048
1049         case ISCSI_PARAM_GET:
1050                 err = iscsi_params_config((void __user *)arg, 0);
1051                 break;
1052
1053         case ADD_CONN:
1054                 err = add_conn((void __user *)arg);
1055                 break;
1056
1057         case DEL_CONN:
1058                 err = del_conn((void __user *)arg);
1059                 break;
1060
1061         default:
1062                 PRINT_ERROR("Invalid ioctl cmd %x", cmd);
1063                 err = -EINVAL;
1064                 goto out_unlock;
1065         }
1066
1067 out_unlock:
1068         mutex_unlock(&target_mgmt_mutex);
1069
1070 out:
1071         TRACE_EXIT_RES(err);
1072         return err;
1073 }
1074
1075 int open(struct inode *inode, struct file *file)
1076 {
1077         bool already;
1078
1079         mutex_lock(&target_mgmt_mutex);
1080         already = (ctr_open_state != ISCSI_CTR_OPEN_STATE_CLOSED);
1081         if (!already)
1082                 ctr_open_state = ISCSI_CTR_OPEN_STATE_OPEN;
1083         mutex_unlock(&target_mgmt_mutex);
1084
1085         if (already) {
1086                 PRINT_WARNING("%s", "Attempt to second open the control "
1087                         "device!");
1088                 return -EBUSY;
1089         } else
1090                 return 0;
1091 }
1092
1093 static int release(struct inode *inode, struct file *filp)
1094 {
1095 #ifndef CONFIG_SCST_PROC
1096         struct iscsi_attr *attr, *t;
1097 #endif
1098
1099         TRACE(TRACE_MGMT, "%s", "Releasing allocated resources");
1100
1101         mutex_lock(&target_mgmt_mutex);
1102         ctr_open_state = ISCSI_CTR_OPEN_STATE_CLOSING;
1103         mutex_unlock(&target_mgmt_mutex);
1104
1105         target_del_all();
1106
1107         mutex_lock(&target_mgmt_mutex);
1108
1109 #ifndef CONFIG_SCST_PROC
1110         list_for_each_entry_safe(attr, t, &iscsi_attrs_list,
1111                                         attrs_list_entry) {
1112                 __iscsi_del_attr(NULL, attr);
1113         }
1114 #endif
1115
1116         ctr_open_state = ISCSI_CTR_OPEN_STATE_CLOSED;
1117
1118         mutex_unlock(&target_mgmt_mutex);
1119
1120         return 0;
1121 }
1122
1123 const struct file_operations ctr_fops = {
1124         .owner          = THIS_MODULE,
1125         .unlocked_ioctl = ioctl,
1126         .compat_ioctl   = ioctl,
1127         .open           = open,
1128         .release        = release,
1129 };
1130
1131 #ifdef CONFIG_SCST_DEBUG
1132 static void iscsi_dump_char(int ch, unsigned char *text, int *pos)
1133 {
1134         int i = *pos;
1135
1136         if (ch < 0) {
1137                 while ((i % 16) != 0) {
1138                         printk(KERN_CONT "   ");
1139                         text[i] = ' ';
1140                         i++;
1141                         if ((i % 16) == 0)
1142                                 printk(KERN_CONT " | %.16s |\n", text);
1143                         else if ((i % 4) == 0)
1144                                 printk(KERN_CONT " |");
1145                 }
1146                 i = 0;
1147                 goto out;
1148         }
1149
1150         text[i] = (ch < 0x20 || (ch >= 0x80 && ch <= 0xa0)) ? ' ' : ch;
1151         printk(KERN_CONT " %02x", ch);
1152         i++;
1153         if ((i % 16) == 0) {
1154                 printk(KERN_CONT " | %.16s |\n", text);
1155                 i = 0;
1156         } else if ((i % 4) == 0)
1157                 printk(KERN_CONT " |");
1158
1159 out:
1160         *pos = i;
1161         return;
1162 }
1163
1164 void iscsi_dump_pdu(struct iscsi_pdu *pdu)
1165 {
1166         unsigned char text[16];
1167         int pos = 0;
1168
1169         if (trace_flag & TRACE_D_DUMP_PDU) {
1170                 unsigned char *buf;
1171                 int i;
1172
1173                 buf = (void *)&pdu->bhs;
1174                 printk(KERN_DEBUG "BHS: (%p,%zd)\n", buf, sizeof(pdu->bhs));
1175                 for (i = 0; i < (int)sizeof(pdu->bhs); i++)
1176                         iscsi_dump_char(*buf++, text, &pos);
1177                 iscsi_dump_char(-1, text, &pos);
1178
1179                 buf = (void *)pdu->ahs;
1180                 printk(KERN_DEBUG "AHS: (%p,%d)\n", buf, pdu->ahssize);
1181                 for (i = 0; i < pdu->ahssize; i++)
1182                         iscsi_dump_char(*buf++, text, &pos);
1183                 iscsi_dump_char(-1, text, &pos);
1184
1185                 printk(KERN_DEBUG "Data: (%d)\n", pdu->datasize);
1186         }
1187 }
1188
1189 unsigned long iscsi_get_flow_ctrl_or_mgmt_dbg_log_flag(struct iscsi_cmnd *cmnd)
1190 {
1191         unsigned long flag;
1192
1193         if (cmnd->cmd_req != NULL)
1194                 cmnd = cmnd->cmd_req;
1195
1196         if (cmnd->scst_cmd == NULL)
1197                 flag = TRACE_MGMT_DEBUG;
1198         else {
1199                 int status = scst_cmd_get_status(cmnd->scst_cmd);
1200                 if ((status == SAM_STAT_TASK_SET_FULL) ||
1201                     (status == SAM_STAT_BUSY))
1202                         flag = TRACE_FLOW_CONTROL;
1203                 else
1204                         flag = TRACE_MGMT_DEBUG;
1205         }
1206         return flag;
1207 }
1208
1209 #endif /* CONFIG_SCST_DEBUG */