734d30404d53f3267e3d61b0e70d9cae488c19d3
[mirror/scst/.git] / scst / src / scst_proc.c
1 /*
2  *  scst_proc.c
3  *
4  *  Copyright (C) 2004 - 2009 Vladislav Bolkhovitin <vst@vlnb.net>
5  *  Copyright (C) 2004 - 2005 Leonid Stoljar
6  *  Copyright (C) 2007 - 2009 ID7 Ltd.
7  *
8  *  This program is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU General Public License
10  *  as published by the Free Software Foundation, version 2
11  *  of the License.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  *  GNU General Public License for more details.
17  */
18
19 #include <linux/module.h>
20
21 #include <linux/init.h>
22 #include <linux/kernel.h>
23 #include <linux/errno.h>
24 #include <linux/list.h>
25 #include <linux/spinlock.h>
26 #include <linux/slab.h>
27 #include <linux/sched.h>
28 #include <linux/unistd.h>
29 #include <linux/string.h>
30 #include <linux/proc_fs.h>
31 #include <linux/seq_file.h>
32
33 #include "scst.h"
34 #include "scst_priv.h"
35 #include "scst_mem.h"
36
37 static int scst_proc_init_groups(void);
38 static void scst_proc_cleanup_groups(void);
39 static int scst_proc_assign_handler(char *buf);
40 static int scst_proc_group_add(const char *p);
41 static int scst_proc_del_free_acg(struct scst_acg *acg, int remove_proc);
42
43 static struct scst_proc_data scst_version_proc_data;
44 static struct scst_proc_data scst_help_proc_data;
45 static struct scst_proc_data scst_sgv_proc_data;
46 static struct scst_proc_data scst_groups_names_proc_data;
47 static struct scst_proc_data scst_groups_devices_proc_data;
48 static struct scst_proc_data scst_sessions_proc_data;
49 static struct scst_proc_data scst_dev_handler_type_proc_data;
50 static struct scst_proc_data scst_tgt_proc_data;
51 static struct scst_proc_data scst_threads_proc_data;
52 static struct scst_proc_data scst_scsi_tgt_proc_data;
53 static struct scst_proc_data scst_dev_handler_proc_data;
54
55 /*
56  * Must be less than 4K page size, since our output routines
57  * use some slack for overruns
58  */
59 #define SCST_PROC_BLOCK_SIZE (PAGE_SIZE - 512)
60
61 #define SCST_PROC_LOG_ENTRY_NAME                "trace_level"
62 #define SCST_PROC_DEV_HANDLER_TYPE_ENTRY_NAME   "type"
63 #define SCST_PROC_VERSION_NAME                  "version"
64 #define SCST_PROC_SESSIONS_NAME                 "sessions"
65 #define SCST_PROC_HELP_NAME                     "help"
66 #define SCST_PROC_THREADS_NAME                  "threads"
67 #define SCST_PROC_GROUPS_ENTRY_NAME             "groups"
68 #define SCST_PROC_GROUPS_DEVICES_ENTRY_NAME     "devices"
69 #define SCST_PROC_GROUPS_USERS_ENTRY_NAME       "names"
70
71 #ifdef CONFIG_SCST_MEASURE_LATENCY
72 #define SCST_PROC_LAT_ENTRY_NAME                "latency"
73 #endif
74
75 #define SCST_PROC_ACTION_ALL             1
76 #define SCST_PROC_ACTION_NONE            2
77 #define SCST_PROC_ACTION_DEFAULT         3
78 #define SCST_PROC_ACTION_SET             4
79 #define SCST_PROC_ACTION_ADD             5
80 #define SCST_PROC_ACTION_CLEAR           6
81 #define SCST_PROC_ACTION_MOVE            7
82 #define SCST_PROC_ACTION_DEL             8
83 #define SCST_PROC_ACTION_VALUE           9
84 #define SCST_PROC_ACTION_ASSIGN         10
85 #define SCST_PROC_ACTION_ADD_GROUP      11
86 #define SCST_PROC_ACTION_DEL_GROUP      12
87 #define SCST_PROC_ACTION_RENAME_GROUP   13
88
89 static struct proc_dir_entry *scst_proc_scsi_tgt;
90 static struct proc_dir_entry *scst_proc_groups_root;
91
92 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
93 static struct scst_proc_data scst_log_proc_data;
94
95 static struct scst_proc_log scst_proc_trace_tbl[] =
96 {
97     { TRACE_OUT_OF_MEM,         "out_of_mem" },
98     { TRACE_MINOR,              "minor" },
99     { TRACE_SG_OP,              "sg" },
100     { TRACE_MEMORY,             "mem" },
101     { TRACE_BUFF,               "buff" },
102     { TRACE_ENTRYEXIT,          "entryexit" },
103     { TRACE_PID,                "pid" },
104     { TRACE_LINE,               "line" },
105     { TRACE_FUNCTION,           "function" },
106     { TRACE_DEBUG,              "debug" },
107     { TRACE_SPECIAL,            "special" },
108     { TRACE_SCSI,               "scsi" },
109     { TRACE_MGMT,               "mgmt" },
110     { TRACE_MGMT_MINOR,         "mgmt_minor" },
111     { TRACE_MGMT_DEBUG,         "mgmt_dbg" },
112     { 0,                        NULL }
113 };
114
115 static struct scst_proc_log scst_proc_local_trace_tbl[] =
116 {
117     { TRACE_RTRY,               "retry" },
118     { TRACE_SCSI_SERIALIZING,   "scsi_serializing" },
119     { TRACE_RCV_BOT,            "recv_bot" },
120     { TRACE_SND_BOT,            "send_bot" },
121     { TRACE_RCV_TOP,            "recv_top" },
122     { TRACE_SND_TOP,            "send_top" },
123     { 0,                        NULL }
124 };
125 #endif
126
127 static char *scst_proc_help_string =
128 "   echo \"assign H:C:I:L HANDLER_NAME\" >/proc/scsi_tgt/scsi_tgt\n"
129 "\n"
130 "   echo \"add_group GROUP_NAME\" >/proc/scsi_tgt/scsi_tgt\n"
131 "   echo \"del_group GROUP_NAME\" >/proc/scsi_tgt/scsi_tgt\n"
132 "   echo \"rename_group OLD_NAME NEW_NAME\" >/proc/scsi_tgt/scsi_tgt\n"
133 "\n"
134 "   echo \"add|del H:C:I:L lun [READ_ONLY]\""
135 " >/proc/scsi_tgt/groups/GROUP_NAME/devices\n"
136 "   echo \"add|del V_NAME lun [READ_ONLY]\""
137 " >/proc/scsi_tgt/groups/GROUP_NAME/devices\n"
138 "   echo \"clear\" >/proc/scsi_tgt/groups/GROUP_NAME/devices\n"
139 "\n"
140 "   echo \"add|del NAME\" >/proc/scsi_tgt/groups/GROUP_NAME/names\n"
141 "   echo \"move NAME NEW_GROUP_NAME\" >/proc/scsi_tgt/groups/OLD_GROUP_NAME/names\n"
142 "   echo \"clear\" >/proc/scsi_tgt/groups/GROUP_NAME/names\n"
143 "\n"
144 "   echo \"DEC|0xHEX|0OCT\" >/proc/scsi_tgt/threads\n"
145 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
146 "\n"
147 "   echo \"all|none|default\" >/proc/scsi_tgt/[DEV_HANDLER_NAME/]trace_level\n"
148 "   echo \"value DEC|0xHEX|0OCT\""
149 " >/proc/scsi_tgt/[DEV_HANDLER_NAME/]trace_level\n"
150 "   echo \"set|add|del TOKEN\""
151 " >/proc/scsi_tgt/[DEV_HANDLER_NAME/]trace_level\n"
152 "     where TOKEN is one of [debug,function,line,pid,entryexit,\n"
153 "                            buff,mem,sg,out_of_mem,special,scsi,mgmt,minor]\n"
154 "     Additionally for /proc/scsi_tgt/trace_level there are these TOKENs\n"
155 "       [scsi_serializing,retry,recv_bot,send_bot,recv_top,send_top]\n"
156 #endif
157 ;
158
159 static char *scst_proc_dev_handler_type[] =
160 {
161     "Direct-access device (e.g., magnetic disk)",
162     "Sequential-access device (e.g., magnetic tape)",
163     "Printer device",
164     "Processor device",
165     "Write-once device (e.g., some optical disks)",
166     "CD-ROM device",
167     "Scanner device (obsolete)",
168     "Optical memory device (e.g., some optical disks)",
169     "Medium changer device (e.g., jukeboxes)",
170     "Communications device (obsolete)",
171     "Defined by ASC IT8 (Graphic arts pre-press devices)",
172     "Defined by ASC IT8 (Graphic arts pre-press devices)",
173     "Storage array controller device (e.g., RAID)",
174     "Enclosure services device",
175     "Simplified direct-access device (e.g., magnetic disk)",
176     "Optical card reader/writer device"
177 };
178
179 static DEFINE_MUTEX(scst_proc_mutex);
180
181 #include <linux/ctype.h>
182
183 #if !defined(CONFIG_PPC) && (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)) && (!defined(RHEL_RELEASE_CODE) || RHEL_RELEASE_CODE -0 < 5 * 256 + 3)
184 /*
185  * If strcasecmp() and strncasecmp() have already been declared in
186  * <linux/string.h>, do not redefine these functions. Declarations for these
187  * functions are present in the <linux/string.h> header of the following
188  * kernels:
189  * - The PPC kernel headers for all kernel versions supported by SCST.
190  * - Kernel version 2.6.22 and later for all architectures.
191  * - RHEL 5.3 and later.
192  *
193  * Notes about the above preprocessor statement:
194  * - We can't use RHEL_RELEASE_CODE(5, 3) because it would trigger an error on
195  *   non-RHEL/CentOS systems -- this expression would expand to "(5,3)".
196  * - There is no space between the minus sign and the zero in the expression
197  *   "RHEL_RELEASE_CODE -0" such that it expands to a syntactically valid
198  *   expression on non-RHEL/CentOS systems ("-0").
199  * - The above statement has been put on one long line because as of r800
200  *   scripts/specialize-patch does not yet handle multi-line preprocessor
201  *   statements correctly.
202  */
203
204 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
205 static int strcasecmp(const char *s1, const char *s2)
206 {
207         int c1, c2;
208         do {
209                 c1 = tolower(*s1++);
210                 c2 = tolower(*s2++);
211         } while (c1 == c2 && c1 != 0);
212         return c1 - c2;
213 }
214 #endif
215
216 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 17)
217 static int strncasecmp(const char *s1, const char *s2, int n)
218 #else
219 static int strncasecmp(const char *s1, const char *s2, size_t n)
220 #endif
221 {
222         int c1, c2;
223         do {
224                 c1 = tolower(*s1++);
225                 c2 = tolower(*s2++);
226         } while ((--n > 0) && c1 == c2 && c1 != 0);
227         return c1 - c2;
228 }
229
230 #endif /* !CONFIG_PPC && (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)) */
231
232 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
233
234 static DEFINE_MUTEX(scst_log_mutex);
235
236 int scst_proc_log_entry_write(struct file *file, const char __user *buf,
237         unsigned long length, unsigned long *log_level,
238         unsigned long default_level, const struct scst_proc_log *tbl)
239 {
240         int res = length;
241         int action;
242         unsigned long level = 0, oldlevel;
243         char *buffer, *p, *e;
244         const struct scst_proc_log *t;
245         char *data = (char *)PDE(file->f_dentry->d_inode)->data;
246
247         TRACE_ENTRY();
248
249         if (length > SCST_PROC_BLOCK_SIZE) {
250                 res = -EOVERFLOW;
251                 goto out;
252         }
253         if (!buf) {
254                 res = -EINVAL;
255                 goto out;
256         }
257         buffer = (char *)__get_free_page(GFP_KERNEL);
258         if (!buffer) {
259                 res = -ENOMEM;
260                 goto out;
261         }
262         if (copy_from_user(buffer, buf, length)) {
263                 res = -EFAULT;
264                 goto out_free;
265         }
266         if (length < PAGE_SIZE) {
267                 buffer[length] = '\0';
268         } else if (buffer[PAGE_SIZE-1]) {
269                 res = -EINVAL;
270                 goto out_free;
271         }
272
273         /*
274          * Usage:
275          *   echo "all|none|default" >/proc/scsi_tgt/trace_level
276          *   echo "value DEC|0xHEX|0OCT" >/proc/scsi_tgt/trace_level
277          *   echo "set|add|clear|del TOKEN" >/proc/scsi_tgt/trace_level
278          * where TOKEN is one of [debug,function,line,pid,entryexit,
279          *                        buff,mem,sg,out_of_mem,retry,
280          *                        scsi_serializing,special,scsi,mgmt,minor,...]
281          */
282         p = buffer;
283         if (!strncasecmp("all", p, 3)) {
284                 action = SCST_PROC_ACTION_ALL;
285         } else if (!strncasecmp("none", p, 4) || !strncasecmp("null", p, 4)) {
286                 action = SCST_PROC_ACTION_NONE;
287         } else if (!strncasecmp("default", p, 7)) {
288                 action = SCST_PROC_ACTION_DEFAULT;
289         } else if (!strncasecmp("set ", p, 4)) {
290                 p += 4;
291                 action = SCST_PROC_ACTION_SET;
292         } else if (!strncasecmp("add ", p, 4)) {
293                 p += 4;
294                 action = SCST_PROC_ACTION_ADD;
295         } else if (!strncasecmp("del ", p, 4)) {
296                 p += 4;
297                 action = SCST_PROC_ACTION_DEL;
298         } else if (!strncasecmp("value ", p, 6)) {
299                 p += 6;
300                 action = SCST_PROC_ACTION_VALUE;
301         } else {
302                 if (p[strlen(p) - 1] == '\n')
303                         p[strlen(p) - 1] = '\0';
304                 PRINT_ERROR("Unknown action \"%s\"", p);
305                 res = -EINVAL;
306                 goto out_free;
307         }
308
309         switch (action) {
310         case SCST_PROC_ACTION_ALL:
311                 level = TRACE_ALL;
312                 break;
313         case SCST_PROC_ACTION_DEFAULT:
314                 level = default_level;
315                 break;
316         case SCST_PROC_ACTION_NONE:
317                 level = TRACE_NULL;
318                 break;
319         case SCST_PROC_ACTION_SET:
320         case SCST_PROC_ACTION_ADD:
321         case SCST_PROC_ACTION_DEL:
322                 while (isspace(*p) && *p != '\0')
323                         p++;
324                 e = p;
325                 while (!isspace(*e) && *e != '\0')
326                         e++;
327                 *e = 0;
328                 if (tbl) {
329                         t = tbl;
330                         while (t->token) {
331                                 if (!strcasecmp(p, t->token)) {
332                                         level = t->val;
333                                         break;
334                                 }
335                                 t++;
336                         }
337                 }
338                 if (level == 0) {
339                         t = scst_proc_trace_tbl;
340                         while (t->token) {
341                                 if (!strcasecmp(p, t->token)) {
342                                         level = t->val;
343                                         break;
344                                 }
345                                 t++;
346                         }
347                 }
348                 if (level == 0) {
349                         PRINT_ERROR("Unknown token \"%s\"", p);
350                         res = -EINVAL;
351                         goto out_free;
352                 }
353                 break;
354         case SCST_PROC_ACTION_VALUE:
355                 while (isspace(*p) && *p != '\0')
356                         p++;
357                 level = simple_strtoul(p, NULL, 0);
358                 break;
359         }
360
361         oldlevel = *log_level;
362
363         switch (action) {
364         case SCST_PROC_ACTION_ADD:
365                 *log_level |= level;
366                 break;
367         case SCST_PROC_ACTION_DEL:
368                 *log_level &= ~level;
369                 break;
370         default:
371                 *log_level = level;
372                 break;
373         }
374
375         PRINT_INFO("Changed trace level for \"%s\": "
376                    "old 0x%08lx, new 0x%08lx",
377                    (char *)data, oldlevel, *log_level);
378
379 out_free:
380         free_page((unsigned long)buffer);
381 out:
382         TRACE_EXIT_RES(res);
383         return res;
384 }
385 EXPORT_SYMBOL(scst_proc_log_entry_write);
386
387 static ssize_t scst_proc_scsi_tgt_gen_write_log(struct file *file,
388                                         const char __user *buf,
389                                         size_t length, loff_t *off)
390 {
391         int res;
392
393         TRACE_ENTRY();
394
395         if (mutex_lock_interruptible(&scst_log_mutex) != 0) {
396                 res = -EINTR;
397                 goto out;
398         }
399
400         res = scst_proc_log_entry_write(file, buf, length,
401                 &trace_flag, SCST_DEFAULT_LOG_FLAGS,
402                 scst_proc_local_trace_tbl);
403
404         mutex_unlock(&scst_log_mutex);
405
406 out:
407         TRACE_EXIT_RES(res);
408         return res;
409 }
410
411 #endif /* defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) */
412
413 #ifdef CONFIG_SCST_MEASURE_LATENCY
414
415 static int lat_info_show(struct seq_file *seq, void *v)
416 {
417         int res = 0;
418         struct scst_acg *acg;
419         struct scst_session *sess;
420
421         TRACE_ENTRY();
422
423         if (mutex_lock_interruptible(&scst_mutex) != 0) {
424                 res = -EINTR;
425                 goto out;
426         }
427
428         seq_printf(seq, "%-20s %-45s %-15s %15s\n",
429                    "Target name",
430                    "Initiator name",
431                    "SCST latency",
432                    "Processing latency (ns)");
433
434         list_for_each_entry(acg, &scst_acg_list, scst_acg_list_entry) {
435                 list_for_each_entry(sess, &acg->acg_sess_list,
436                                 acg_sess_list_entry) {
437                         unsigned long proc_lat = 0, scst_lat = 0;
438                         uint64_t proc_time, scst_time;
439                         unsigned int processed_cmds;
440
441                         spin_lock_bh(&sess->meas_lock);
442                         proc_time = sess->processing_time;
443                         scst_time = sess->scst_time;
444                         processed_cmds = sess->processed_cmds;
445                         spin_unlock_bh(&sess->meas_lock);
446
447                         TRACE_DBG("sess %p, scst_time %lld, proc_time %lld, "
448                                 "processed_cmds %d", sess, scst_time,
449                                 proc_time, processed_cmds);
450
451 #if BITS_PER_LONG == 32
452                         /* Unfortunately, do_div() doesn't work too well */
453                         while (((proc_time & 0xFFFFFFFF00000000LL) != 0) ||
454                                ((scst_time & 0xFFFFFFFF00000000LL) != 0)) {
455                                 TRACE_DBG("%s", "Gathered time too big");
456                                 proc_time >>= 1;
457                                 scst_time >>= 1;
458                                 processed_cmds >>= 1;
459                         }
460 #endif
461
462                         if (sess->processed_cmds != 0) {
463                                 proc_lat = (unsigned long)proc_time /
464                                                 processed_cmds;
465                                 scst_lat = (unsigned long)scst_time /
466                                                 processed_cmds;
467                         }
468
469                         seq_printf(seq, "%-20s %-45s %-15ld %-15ld\n",
470                                         sess->tgt->tgtt->name,
471                                         sess->initiator_name,
472                                         scst_lat, proc_lat);
473                 }
474         }
475
476         mutex_unlock(&scst_mutex);
477
478 out:
479         TRACE_EXIT_RES(res);
480         return res;
481 }
482
483 static ssize_t scst_proc_scsi_tgt_gen_write_lat(struct file *file,
484                                         const char __user *buf,
485                                         size_t length, loff_t *off)
486 {
487         int res = length;
488         struct scst_acg *acg;
489         struct scst_session *sess;
490
491         TRACE_ENTRY();
492
493         if (mutex_lock_interruptible(&scst_mutex) != 0) {
494                 res = -EINTR;
495                 goto out;
496         }
497
498         list_for_each_entry(acg, &scst_acg_list, scst_acg_list_entry) {
499                 list_for_each_entry(sess, &acg->acg_sess_list,
500                                 acg_sess_list_entry) {
501                         PRINT_INFO("Zeroing latency statistics for initiator"
502                                 " %s",
503                                 sess->initiator_name);
504                         spin_lock_bh(&sess->meas_lock);
505                         sess->processing_time = 0;
506                         sess->scst_time = 0;
507                         sess->processed_cmds = 0;
508                         spin_unlock_bh(&sess->meas_lock);
509                 }
510         }
511
512         mutex_unlock(&scst_mutex);
513
514 out:
515         TRACE_EXIT_RES(res);
516         return res;
517 }
518
519 static struct scst_proc_data scst_lat_proc_data = {
520         SCST_DEF_RW_SEQ_OP(scst_proc_scsi_tgt_gen_write_lat)
521         .show = lat_info_show,
522         .data = "scsi_tgt",
523 };
524
525 #endif /* CONFIG_SCST_MEASURE_LATENCY */
526
527 static int __init scst_proc_init_module_log(void)
528 {
529         int res = 0;
530 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) || \
531     defined(CONFIG_SCST_MEASURE_LATENCY)
532         struct proc_dir_entry *generic;
533 #endif
534
535         TRACE_ENTRY();
536
537 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
538         generic = scst_create_proc_entry(scst_proc_scsi_tgt,
539                                          SCST_PROC_LOG_ENTRY_NAME,
540                                          &scst_log_proc_data);
541         if (!generic) {
542                 PRINT_ERROR("cannot init /proc/%s/%s",
543                             SCST_PROC_ENTRY_NAME, SCST_PROC_LOG_ENTRY_NAME);
544                 res = -ENOMEM;
545         }
546 #endif
547
548 #ifdef CONFIG_SCST_MEASURE_LATENCY
549         if (res == 0) {
550                 generic = scst_create_proc_entry(scst_proc_scsi_tgt,
551                                          SCST_PROC_LAT_ENTRY_NAME,
552                                          &scst_lat_proc_data);
553                 if (!generic) {
554                         PRINT_ERROR("cannot init /proc/%s/%s",
555                                     SCST_PROC_ENTRY_NAME,
556                                     SCST_PROC_LAT_ENTRY_NAME);
557                         res = -ENOMEM;
558                 }
559         }
560 #endif
561
562         TRACE_EXIT_RES(res);
563         return res;
564 }
565
566 static void scst_proc_cleanup_module_log(void)
567 {
568         TRACE_ENTRY();
569
570 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
571         remove_proc_entry(SCST_PROC_LOG_ENTRY_NAME, scst_proc_scsi_tgt);
572 #endif
573
574 #ifdef CONFIG_SCST_MEASURE_LATENCY
575         remove_proc_entry(SCST_PROC_LAT_ENTRY_NAME, scst_proc_scsi_tgt);
576 #endif
577
578         TRACE_EXIT();
579         return;
580 }
581
582 static int scst_proc_group_add_tree(struct scst_acg *acg, const char *name)
583 {
584         int res = 0;
585         struct proc_dir_entry *generic;
586
587         TRACE_ENTRY();
588
589         acg->acg_proc_root = proc_mkdir(name, scst_proc_groups_root);
590         if (acg->acg_proc_root == NULL) {
591                 PRINT_ERROR("Not enough memory to register %s entry in "
592                                "/proc/%s/%s", name, SCST_PROC_ENTRY_NAME,
593                                SCST_PROC_GROUPS_ENTRY_NAME);
594                 goto out;
595         }
596
597         scst_groups_devices_proc_data.data = acg;
598         generic = scst_create_proc_entry(acg->acg_proc_root,
599                                          SCST_PROC_GROUPS_DEVICES_ENTRY_NAME,
600                                          &scst_groups_devices_proc_data);
601         if (!generic) {
602                 PRINT_ERROR("cannot init /proc/%s/%s/%s/%s",
603                                SCST_PROC_ENTRY_NAME,
604                                SCST_PROC_GROUPS_ENTRY_NAME,
605                                name, SCST_PROC_GROUPS_DEVICES_ENTRY_NAME);
606                 res = -ENOMEM;
607                 goto out_remove;
608         }
609
610         scst_groups_names_proc_data.data = acg;
611         generic = scst_create_proc_entry(acg->acg_proc_root,
612                                          SCST_PROC_GROUPS_USERS_ENTRY_NAME,
613                                          &scst_groups_names_proc_data);
614         if (!generic) {
615                 PRINT_ERROR("cannot init /proc/%s/%s/%s/%s",
616                                SCST_PROC_ENTRY_NAME,
617                                SCST_PROC_GROUPS_ENTRY_NAME,
618                                name, SCST_PROC_GROUPS_USERS_ENTRY_NAME);
619                 res = -ENOMEM;
620                 goto out_remove1;
621         }
622
623 out:
624         TRACE_EXIT_RES(res);
625         return res;
626
627 out_remove1:
628         remove_proc_entry(SCST_PROC_GROUPS_DEVICES_ENTRY_NAME,
629                           acg->acg_proc_root);
630
631 out_remove:
632         remove_proc_entry(name, scst_proc_groups_root);
633         goto out;
634 }
635
636 static void scst_proc_del_acg_tree(struct proc_dir_entry *acg_proc_root,
637         const char *name)
638 {
639         TRACE_ENTRY();
640
641         remove_proc_entry(SCST_PROC_GROUPS_USERS_ENTRY_NAME, acg_proc_root);
642         remove_proc_entry(SCST_PROC_GROUPS_DEVICES_ENTRY_NAME,
643                                 acg_proc_root);
644         remove_proc_entry(name, scst_proc_groups_root);
645
646         TRACE_EXIT();
647         return;
648 }
649
650 /* The activity supposed to be suspended and scst_mutex held */
651 static int scst_proc_group_add(const char *p)
652 {
653         int res = 0, len = strlen(p) + 1;
654         struct scst_acg *acg;
655         char *name = NULL;
656
657         TRACE_ENTRY();
658
659         name = kmalloc(len, GFP_KERNEL);
660         if (name == NULL) {
661                 TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of name failed");
662                 goto out_nomem;
663         }
664         strncpy(name, p, len);
665
666         acg = scst_alloc_add_acg(name);
667         if (acg == NULL) {
668                 PRINT_ERROR("scst_alloc_add_acg() (name %s) failed", name);
669                 goto out_free;
670         }
671
672         res = scst_proc_group_add_tree(acg, p);
673         if (res != 0)
674                 goto out_free_acg;
675
676 out:
677         TRACE_EXIT_RES(res);
678         return res;
679
680 out_free_acg:
681         scst_proc_del_free_acg(acg, 0);
682
683 out_free:
684         kfree(name);
685
686 out_nomem:
687         res = -ENOMEM;
688         goto out;
689 }
690
691 /* The activity supposed to be suspended and scst_mutex held */
692 static int scst_proc_del_free_acg(struct scst_acg *acg, int remove_proc)
693 {
694         const char *name;
695         struct proc_dir_entry *acg_proc_root = acg->acg_proc_root;
696         int res = 0;
697
698         TRACE_ENTRY();
699
700         if (acg != scst_default_acg) {
701                 name = acg->acg_name;
702                 res = scst_destroy_acg(acg);
703                 if (res == 0) {
704                         if (remove_proc)
705                                 scst_proc_del_acg_tree(acg_proc_root, name);
706                         kfree(name);
707                 }
708         }
709
710         TRACE_EXIT_RES(res);
711         return res;
712 }
713
714 /* The activity supposed to be suspended and scst_mutex held */
715 static int scst_proc_rename_acg(struct scst_acg *acg, const char *new_name)
716 {
717         int res = 0, len = strlen(new_name) + 1;
718         char *name;
719         struct proc_dir_entry *old_acg_proc_root = acg->acg_proc_root;
720
721         TRACE_ENTRY();
722
723         name = kmalloc(len, GFP_KERNEL);
724         if (name == NULL) {
725                 TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of new name failed");
726                 goto out_nomem;
727         }
728         strncpy(name, new_name, len);
729
730         res = scst_proc_group_add_tree(acg, new_name);
731         if (res != 0)
732                 goto out_free;
733
734         scst_proc_del_acg_tree(old_acg_proc_root, acg->acg_name);
735
736         kfree(acg->acg_name);
737         acg->acg_name = name;
738
739         scst_check_reassign_sessions();
740
741 out:
742         TRACE_EXIT_RES(res);
743         return res;
744
745 out_free:
746         kfree(name);
747
748 out_nomem:
749         res = -ENOMEM;
750         goto out;
751 }
752
753 static int __init scst_proc_init_groups(void)
754 {
755         int res = 0;
756
757         TRACE_ENTRY();
758
759         /* create the proc directory entry for the device */
760         scst_proc_groups_root = proc_mkdir(SCST_PROC_GROUPS_ENTRY_NAME,
761                                            scst_proc_scsi_tgt);
762         if (scst_proc_groups_root == NULL) {
763                 PRINT_ERROR("Not enough memory to register %s entry in "
764                                "/proc/%s", SCST_PROC_GROUPS_ENTRY_NAME,
765                                SCST_PROC_ENTRY_NAME);
766                 goto out_nomem;
767         }
768
769         res = scst_proc_group_add_tree(scst_default_acg,
770                                         SCST_DEFAULT_ACG_NAME);
771         if (res != 0)
772                 goto out_remove;
773
774 out:
775         TRACE_EXIT_RES(res);
776         return res;
777
778 out_remove:
779         remove_proc_entry(SCST_PROC_GROUPS_ENTRY_NAME, scst_proc_scsi_tgt);
780
781 out_nomem:
782         res = -ENOMEM;
783         goto out;
784 }
785
786 static void scst_proc_cleanup_groups(void)
787 {
788         struct scst_acg *acg_tmp, *acg;
789
790         TRACE_ENTRY();
791
792         /* remove all groups (dir & entries) */
793         list_for_each_entry_safe(acg, acg_tmp, &scst_acg_list,
794                                  scst_acg_list_entry) {
795                 scst_proc_del_free_acg(acg, 1);
796         }
797
798         scst_proc_del_acg_tree(scst_default_acg->acg_proc_root,
799                                 SCST_DEFAULT_ACG_NAME);
800         TRACE_DBG("remove_proc_entry(%s, %p)",
801                   SCST_PROC_GROUPS_ENTRY_NAME, scst_proc_scsi_tgt);
802         remove_proc_entry(SCST_PROC_GROUPS_ENTRY_NAME, scst_proc_scsi_tgt);
803
804         TRACE_EXIT();
805 }
806
807 static int __init scst_proc_init_sgv(void)
808 {
809         int res = 0;
810         struct proc_dir_entry *pr;
811
812         TRACE_ENTRY();
813
814         pr = scst_create_proc_entry(scst_proc_scsi_tgt, "sgv",
815                                 &scst_sgv_proc_data);
816         if (pr == NULL) {
817                 PRINT_ERROR("%s", "cannot create sgv /proc entry");
818                 res = -ENOMEM;
819         }
820
821         TRACE_EXIT_RES(res);
822         return res;
823 }
824
825 static void __exit scst_proc_cleanup_sgv(void)
826 {
827         TRACE_ENTRY();
828         remove_proc_entry("sgv", scst_proc_scsi_tgt);
829         TRACE_EXIT();
830 }
831
832 int __init scst_proc_init_module(void)
833 {
834         int res = 0;
835         struct proc_dir_entry *generic;
836
837         TRACE_ENTRY();
838
839         scst_proc_scsi_tgt = proc_mkdir(SCST_PROC_ENTRY_NAME, NULL);
840         if (!scst_proc_scsi_tgt) {
841                 PRINT_ERROR("cannot init /proc/%s", SCST_PROC_ENTRY_NAME);
842                 goto out_nomem;
843         }
844
845         generic = scst_create_proc_entry(scst_proc_scsi_tgt,
846                                          SCST_PROC_ENTRY_NAME,
847                                          &scst_tgt_proc_data);
848         if (!generic) {
849                 PRINT_ERROR("cannot init /proc/%s/%s",
850                             SCST_PROC_ENTRY_NAME, SCST_PROC_ENTRY_NAME);
851                 goto out_remove;
852         }
853
854         generic = scst_create_proc_entry(scst_proc_scsi_tgt,
855                                          SCST_PROC_VERSION_NAME,
856                                          &scst_version_proc_data);
857         if (!generic) {
858                 PRINT_ERROR("cannot init /proc/%s/%s",
859                             SCST_PROC_ENTRY_NAME, SCST_PROC_VERSION_NAME);
860                 goto out_remove1;
861         }
862
863         generic = scst_create_proc_entry(scst_proc_scsi_tgt,
864                                          SCST_PROC_SESSIONS_NAME,
865                                          &scst_sessions_proc_data);
866         if (!generic) {
867                 PRINT_ERROR("cannot init /proc/%s/%s",
868                             SCST_PROC_ENTRY_NAME, SCST_PROC_SESSIONS_NAME);
869                 goto out_remove2;
870         }
871
872         generic = scst_create_proc_entry(scst_proc_scsi_tgt,
873                                          SCST_PROC_HELP_NAME,
874                                          &scst_help_proc_data);
875         if (!generic) {
876                 PRINT_ERROR("cannot init /proc/%s/%s",
877                             SCST_PROC_ENTRY_NAME, SCST_PROC_HELP_NAME);
878                 goto out_remove3;
879         }
880
881         generic = scst_create_proc_entry(scst_proc_scsi_tgt,
882                                          SCST_PROC_THREADS_NAME,
883                                          &scst_threads_proc_data);
884         if (!generic) {
885                 PRINT_ERROR("cannot init /proc/%s/%s",
886                             SCST_PROC_ENTRY_NAME, SCST_PROC_THREADS_NAME);
887                 goto out_remove4;
888         }
889
890         if (scst_proc_init_module_log() < 0)
891                 goto out_remove5;
892
893         if (scst_proc_init_groups() < 0)
894                 goto out_remove6;
895
896         if (scst_proc_init_sgv() < 0)
897                 goto out_remove7;
898
899 out:
900         TRACE_EXIT_RES(res);
901         return res;
902
903 out_remove7:
904         scst_proc_cleanup_groups();
905
906 out_remove6:
907         scst_proc_cleanup_module_log();
908
909 out_remove5:
910         remove_proc_entry(SCST_PROC_THREADS_NAME, scst_proc_scsi_tgt);
911
912 out_remove4:
913         remove_proc_entry(SCST_PROC_HELP_NAME, scst_proc_scsi_tgt);
914
915 out_remove3:
916         remove_proc_entry(SCST_PROC_SESSIONS_NAME, scst_proc_scsi_tgt);
917
918 out_remove2:
919         remove_proc_entry(SCST_PROC_VERSION_NAME, scst_proc_scsi_tgt);
920
921 out_remove1:
922         remove_proc_entry(SCST_PROC_ENTRY_NAME, scst_proc_scsi_tgt);
923
924 out_remove:
925         remove_proc_entry(SCST_PROC_ENTRY_NAME, NULL);
926
927 out_nomem:
928         res = -ENOMEM;
929         goto out;
930 }
931
932 void __exit scst_proc_cleanup_module(void)
933 {
934         TRACE_ENTRY();
935
936         /* We may not bother about locks here */
937         scst_proc_cleanup_sgv();
938         scst_proc_cleanup_groups();
939         scst_proc_cleanup_module_log();
940         remove_proc_entry(SCST_PROC_THREADS_NAME, scst_proc_scsi_tgt);
941         remove_proc_entry(SCST_PROC_HELP_NAME, scst_proc_scsi_tgt);
942         remove_proc_entry(SCST_PROC_SESSIONS_NAME, scst_proc_scsi_tgt);
943         remove_proc_entry(SCST_PROC_VERSION_NAME, scst_proc_scsi_tgt);
944         remove_proc_entry(SCST_PROC_ENTRY_NAME, scst_proc_scsi_tgt);
945         remove_proc_entry(SCST_PROC_ENTRY_NAME, NULL);
946
947         TRACE_EXIT();
948 }
949
950 static ssize_t scst_proc_threads_write(struct file *file,
951                                        const char __user *buf,
952                                        size_t length, loff_t *off)
953 {
954         int res = length;
955         int oldtn, newtn, delta;
956         char *buffer;
957
958         TRACE_ENTRY();
959
960         if (length > SCST_PROC_BLOCK_SIZE) {
961                 res = -EOVERFLOW;
962                 goto out;
963         }
964         if (!buf) {
965                 res = -EINVAL;
966                 goto out;
967         }
968         buffer = (char *)__get_free_page(GFP_KERNEL);
969         if (!buffer) {
970                 res = -ENOMEM;
971                 goto out;
972         }
973         if (copy_from_user(buffer, buf, length)) {
974                 res = -EFAULT;
975                 goto out_free;
976         }
977         if (length < PAGE_SIZE) {
978                 buffer[length] = '\0';
979         } else if (buffer[PAGE_SIZE-1]) {
980                 res = -EINVAL;
981                 goto out_free;
982         }
983
984         if (mutex_lock_interruptible(&scst_proc_mutex) != 0) {
985                 res = -EINTR;
986                 goto out_free;
987         }
988
989         mutex_lock(&scst_global_threads_mutex);
990
991         oldtn = scst_nr_global_threads;
992         newtn = simple_strtoul(buffer, NULL, 0);
993         if (newtn <= 0) {
994                 PRINT_ERROR("Illegal threads num value %d", newtn);
995                 res = -EINVAL;
996                 goto out_up_thr_free;
997         }
998         delta = newtn - oldtn;
999         if (delta < 0)
1000                 __scst_del_global_threads(-delta);
1001         else
1002                 __scst_add_global_threads(delta);
1003
1004         PRINT_INFO("Changed cmd threads num: old %d, new %d", oldtn, newtn);
1005
1006 out_up_thr_free:
1007         mutex_unlock(&scst_global_threads_mutex);
1008
1009         mutex_unlock(&scst_proc_mutex);
1010
1011 out_free:
1012         free_page((unsigned long)buffer);
1013 out:
1014         TRACE_EXIT_RES(res);
1015         return res;
1016 }
1017
1018 int scst_build_proc_target_dir_entries(struct scst_tgt_template *vtt)
1019 {
1020         int res = 0;
1021
1022         TRACE_ENTRY();
1023
1024         /* create the proc directory entry for the device */
1025         vtt->proc_tgt_root = proc_mkdir(vtt->name, scst_proc_scsi_tgt);
1026         if (vtt->proc_tgt_root == NULL) {
1027                 PRINT_ERROR("Not enough memory to register SCSI target %s "
1028                     "in /proc/%s", vtt->name, SCST_PROC_ENTRY_NAME);
1029                 goto out_nomem;
1030         }
1031
1032 out:
1033         TRACE_EXIT_RES(res);
1034         return res;
1035
1036 out_nomem:
1037         res = -ENOMEM;
1038         goto out;
1039 }
1040
1041 void scst_cleanup_proc_target_dir_entries(struct scst_tgt_template *vtt)
1042 {
1043         TRACE_ENTRY();
1044
1045         remove_proc_entry(vtt->name, scst_proc_scsi_tgt);
1046
1047         TRACE_EXIT();
1048         return;
1049 }
1050
1051 /* Called under scst_mutex */
1052 int scst_build_proc_target_entries(struct scst_tgt *vtt)
1053 {
1054         int res = 0;
1055         struct proc_dir_entry *p;
1056         char name[20];
1057
1058         TRACE_ENTRY();
1059
1060         if (vtt->tgtt->read_proc || vtt->tgtt->write_proc) {
1061                 /* create the proc file entry for the device */
1062                 scnprintf(name, sizeof(name), "%d", vtt->tgtt->proc_dev_num);
1063                 scst_scsi_tgt_proc_data.data = (void *)vtt;
1064                 p = scst_create_proc_entry(vtt->tgtt->proc_tgt_root,
1065                                            name,
1066                                            &scst_scsi_tgt_proc_data);
1067                 if (p == NULL) {
1068                         PRINT_ERROR("Not enough memory to register SCSI "
1069                              "target entry %s in /proc/%s/%s", name,
1070                              SCST_PROC_ENTRY_NAME, vtt->tgtt->name);
1071                         res = -ENOMEM;
1072                         goto out;
1073                 }
1074                 vtt->proc_num = vtt->tgtt->proc_dev_num;
1075                 vtt->tgtt->proc_dev_num++;
1076         }
1077
1078 out:
1079         TRACE_EXIT_RES(res);
1080         return res;
1081 }
1082
1083 void scst_cleanup_proc_target_entries(struct scst_tgt *vtt)
1084 {
1085         char name[20];
1086
1087         TRACE_ENTRY();
1088
1089         if (vtt->tgtt->read_proc || vtt->tgtt->write_proc) {
1090                 scnprintf(name, sizeof(name), "%d", vtt->proc_num);
1091                 remove_proc_entry(name, vtt->tgtt->proc_tgt_root);
1092         }
1093
1094         TRACE_EXIT();
1095         return;
1096 }
1097
1098 static ssize_t scst_proc_scsi_tgt_write(struct file *file,
1099                                         const char __user *buf,
1100                                         size_t length, loff_t *off)
1101 {
1102         struct scst_tgt *vtt =
1103                 (struct scst_tgt *)PDE(file->f_dentry->d_inode)->data;
1104         ssize_t res = 0;
1105         char *buffer;
1106         char *start;
1107         int eof = 0;
1108
1109         TRACE_ENTRY();
1110
1111         if (vtt->tgtt->write_proc == NULL) {
1112                 res = -ENOSYS;
1113                 goto out;
1114         }
1115
1116         if (length > SCST_PROC_BLOCK_SIZE) {
1117                 res = -EOVERFLOW;
1118                 goto out;
1119         }
1120         if (!buf) {
1121                 res = -EINVAL;
1122                 goto out;
1123         }
1124         buffer = (char *)__get_free_page(GFP_KERNEL);
1125         if (!buffer) {
1126                 res = -ENOMEM;
1127                 goto out;
1128         }
1129         if (copy_from_user(buffer, buf, length)) {
1130                 res = -EFAULT;
1131                 goto out_free;
1132         }
1133         if (length < PAGE_SIZE) {
1134                 buffer[length] = '\0';
1135         } else if (buffer[PAGE_SIZE-1]) {
1136                 res = -EINVAL;
1137                 goto out_free;
1138         }
1139
1140         TRACE_BUFFER("Buffer", buffer, length);
1141
1142         if (mutex_lock_interruptible(&scst_proc_mutex) != 0) {
1143                 res = -EINTR;
1144                 goto out_free;
1145         }
1146
1147         res = vtt->tgtt->write_proc(buffer, &start, 0, length, &eof, vtt);
1148
1149         mutex_unlock(&scst_proc_mutex);
1150
1151 out_free:
1152         free_page((unsigned long)buffer);
1153 out:
1154         TRACE_EXIT_RES(res);
1155         return res;
1156 }
1157
1158 int scst_build_proc_dev_handler_dir_entries(struct scst_dev_type *dev_type)
1159 {
1160         int res = 0;
1161         struct proc_dir_entry *p;
1162
1163         TRACE_ENTRY();
1164
1165         sBUG_ON(dev_type->proc_dev_type_root);
1166
1167         /* create the proc directory entry for the dev type handler */
1168         dev_type->proc_dev_type_root = proc_mkdir(dev_type->name,
1169                                                   scst_proc_scsi_tgt);
1170         if (dev_type->proc_dev_type_root == NULL) {
1171                 PRINT_ERROR("Not enough memory to register dev handler dir "
1172                     "%s in /proc/%s", dev_type->name, SCST_PROC_ENTRY_NAME);
1173                 goto out_nomem;
1174         }
1175
1176         scst_dev_handler_type_proc_data.data = dev_type;
1177         if (dev_type->type >= 0) {
1178                 p = scst_create_proc_entry(dev_type->proc_dev_type_root,
1179                                    SCST_PROC_DEV_HANDLER_TYPE_ENTRY_NAME,
1180                                    &scst_dev_handler_type_proc_data);
1181                 if (p == NULL) {
1182                         PRINT_ERROR("Not enough memory to register dev "
1183                              "handler entry %s in /proc/%s/%s",
1184                              SCST_PROC_DEV_HANDLER_TYPE_ENTRY_NAME,
1185                              SCST_PROC_ENTRY_NAME, dev_type->name);
1186                         goto out_remove;
1187                 }
1188         }
1189
1190         if (dev_type->read_proc || dev_type->write_proc) {
1191                 /* create the proc file entry for the dev type handler */
1192                 scst_dev_handler_proc_data.data = (void *)dev_type;
1193                 p = scst_create_proc_entry(dev_type->proc_dev_type_root,
1194                                            dev_type->name,
1195                                            &scst_dev_handler_proc_data);
1196                 if (p == NULL) {
1197                         PRINT_ERROR("Not enough memory to register dev "
1198                              "handler entry %s in /proc/%s/%s", dev_type->name,
1199                              SCST_PROC_ENTRY_NAME, dev_type->name);
1200                         goto out_remove1;
1201                 }
1202         }
1203
1204 out:
1205         TRACE_EXIT_RES(res);
1206         return res;
1207
1208 out_remove1:
1209         if (dev_type->type >= 0)
1210                 remove_proc_entry(SCST_PROC_DEV_HANDLER_TYPE_ENTRY_NAME,
1211                                   dev_type->proc_dev_type_root);
1212
1213 out_remove:
1214         remove_proc_entry(dev_type->name, scst_proc_scsi_tgt);
1215
1216 out_nomem:
1217         res = -ENOMEM;
1218         goto out;
1219 }
1220
1221 void scst_cleanup_proc_dev_handler_dir_entries(struct scst_dev_type *dev_type)
1222 {
1223         TRACE_ENTRY();
1224
1225         sBUG_ON(dev_type->proc_dev_type_root == NULL);
1226
1227         if (dev_type->type >= 0) {
1228                 remove_proc_entry(SCST_PROC_DEV_HANDLER_TYPE_ENTRY_NAME,
1229                                   dev_type->proc_dev_type_root);
1230         }
1231         if (dev_type->read_proc || dev_type->write_proc)
1232                 remove_proc_entry(dev_type->name, dev_type->proc_dev_type_root);
1233         remove_proc_entry(dev_type->name, scst_proc_scsi_tgt);
1234         dev_type->proc_dev_type_root = NULL;
1235
1236         TRACE_EXIT();
1237         return;
1238 }
1239
1240 static ssize_t scst_proc_scsi_dev_handler_write(struct file *file,
1241                                                 const char __user *buf,
1242                                                 size_t length, loff_t *off)
1243 {
1244         struct scst_dev_type *dev_type =
1245                 (struct scst_dev_type *)PDE(file->f_dentry->d_inode)->data;
1246         ssize_t res = 0;
1247         char *buffer;
1248         char *start;
1249         int eof = 0;
1250
1251         TRACE_ENTRY();
1252
1253         if (dev_type->write_proc == NULL) {
1254                 res = -ENOSYS;
1255                 goto out;
1256         }
1257
1258         if (length > SCST_PROC_BLOCK_SIZE) {
1259                 res = -EOVERFLOW;
1260                 goto out;
1261         }
1262         if (!buf) {
1263                 res = -EINVAL;
1264                 goto out;
1265         }
1266
1267         buffer = (char *)__get_free_page(GFP_KERNEL);
1268         if (!buffer) {
1269                 res = -ENOMEM;
1270                 goto out;
1271         }
1272
1273         if (copy_from_user(buffer, buf, length)) {
1274                 res = -EFAULT;
1275                 goto out_free;
1276         }
1277         if (length < PAGE_SIZE) {
1278                 buffer[length] = '\0';
1279         } else if (buffer[PAGE_SIZE-1]) {
1280                 res = -EINVAL;
1281                 goto out_free;
1282         }
1283
1284         TRACE_BUFFER("Buffer", buffer, length);
1285
1286         if (mutex_lock_interruptible(&scst_proc_mutex) != 0) {
1287                 res = -EINTR;
1288                 goto out_free;
1289         }
1290
1291         res = dev_type->write_proc(buffer, &start, 0, length, &eof, dev_type);
1292
1293         mutex_unlock(&scst_proc_mutex);
1294
1295 out_free:
1296         free_page((unsigned long)buffer);
1297
1298 out:
1299         TRACE_EXIT_RES(res);
1300         return res;
1301 }
1302
1303 static ssize_t scst_proc_scsi_tgt_gen_write(struct file *file,
1304                                         const char __user *buf,
1305                                         size_t length, loff_t *off)
1306 {
1307         int res, rc = 0, action;
1308         char *buffer, *p, *pp;
1309         struct scst_acg *a, *acg = NULL;
1310
1311         TRACE_ENTRY();
1312
1313         if (length > SCST_PROC_BLOCK_SIZE) {
1314                 res = -EOVERFLOW;
1315                 goto out;
1316         }
1317         if (!buf) {
1318                 res = -EINVAL;
1319                 goto out;
1320         }
1321         buffer = (char *)__get_free_page(GFP_KERNEL);
1322         if (!buffer) {
1323                 res = -ENOMEM;
1324                 goto out;
1325         }
1326         if (copy_from_user(buffer, buf, length)) {
1327                 res = -EFAULT;
1328                 goto out_free;
1329         }
1330         if (length < PAGE_SIZE) {
1331                 buffer[length] = '\0';
1332         } else if (buffer[PAGE_SIZE-1]) {
1333                 res = -EINVAL;
1334                 goto out_free;
1335         }
1336
1337         /*
1338          * Usage: echo "add_group GROUP_NAME" >/proc/scsi_tgt/scsi_tgt
1339          *   or   echo "del_group GROUP_NAME" >/proc/scsi_tgt/scsi_tgt
1340          *   or   echo "rename_group OLD_NAME NEW_NAME" >/proc/scsi_tgt/scsi_tgt"
1341          *   or   echo "assign H:C:I:L HANDLER_NAME" >/proc/scsi_tgt/scsi_tgt
1342          */
1343         p = buffer;
1344         if (p[strlen(p) - 1] == '\n')
1345                 p[strlen(p) - 1] = '\0';
1346         if (!strncasecmp("assign ", p, 7)) {
1347                 p += 7;
1348                 action = SCST_PROC_ACTION_ASSIGN;
1349         } else if (!strncasecmp("add_group ", p, 10)) {
1350                 p += 10;
1351                 action = SCST_PROC_ACTION_ADD_GROUP;
1352         } else if (!strncasecmp("del_group ", p, 10)) {
1353                 p += 10;
1354                 action = SCST_PROC_ACTION_DEL_GROUP;
1355         } else if (!strncasecmp("rename_group ", p, 13)) {
1356                 p += 13;
1357                 action = SCST_PROC_ACTION_RENAME_GROUP;
1358         } else {
1359                 PRINT_ERROR("Unknown action \"%s\"", p);
1360                 res = -EINVAL;
1361                 goto out_free;
1362         }
1363
1364         res = scst_suspend_activity(true);
1365         if (res != 0)
1366                 goto out_free;
1367
1368         if (mutex_lock_interruptible(&scst_mutex) != 0) {
1369                 res = -EINTR;
1370                 goto out_free_resume;
1371         }
1372
1373         res = length;
1374
1375         switch (action) {
1376         case SCST_PROC_ACTION_ADD_GROUP:
1377         case SCST_PROC_ACTION_DEL_GROUP:
1378         case SCST_PROC_ACTION_RENAME_GROUP:
1379                 pp = p;
1380                 while (!isspace(*pp) && *pp != '\0')
1381                         pp++;
1382                 if (*pp != '\0') {
1383                         *pp = '\0';
1384                         pp++;
1385                         while (isspace(*pp) && *pp != '\0')
1386                                 pp++;
1387                         if (*pp != '\0') {
1388                                 switch (action) {
1389                                 case SCST_PROC_ACTION_ADD_GROUP:
1390                                 case SCST_PROC_ACTION_DEL_GROUP:
1391                                         PRINT_ERROR("%s", "Too many "
1392                                                 "arguments");
1393                                         res = -EINVAL;
1394                                         goto out_up_free;
1395                                 }
1396                         }
1397                 }
1398
1399                 if (strcmp(p, SCST_DEFAULT_ACG_NAME) == 0) {
1400                         PRINT_ERROR("Attempt to add/delete/rename predefined "
1401                                 "group \"%s\"", p);
1402                         res = -EINVAL;
1403                         goto out_up_free;
1404                 }
1405
1406                 list_for_each_entry(a, &scst_acg_list, scst_acg_list_entry) {
1407                         if (strcmp(a->acg_name, p) == 0) {
1408                                 TRACE_DBG("group (acg) %p %s found",
1409                                           a, a->acg_name);
1410                                 acg = a;
1411                                 break;
1412                         }
1413                 }
1414
1415                 switch (action) {
1416                 case SCST_PROC_ACTION_ADD_GROUP:
1417                         if (acg) {
1418                                 PRINT_ERROR("acg name %s exist", p);
1419                                 res = -EINVAL;
1420                                 goto out_up_free;
1421                         }
1422                         rc = scst_proc_group_add(p);
1423                         break;
1424                 case SCST_PROC_ACTION_DEL_GROUP:
1425                         if (acg == NULL) {
1426                                 PRINT_ERROR("acg name %s not found", p);
1427                                 res = -EINVAL;
1428                                 goto out_up_free;
1429                         }
1430                         rc = scst_proc_del_free_acg(acg, 1);
1431                         break;
1432                 case SCST_PROC_ACTION_RENAME_GROUP:
1433                         if (acg == NULL) {
1434                                 PRINT_ERROR("acg name %s not found", p);
1435                                 res = -EINVAL;
1436                                 goto out_up_free;
1437                         }
1438
1439                         p = pp;
1440                         while (!isspace(*pp) && *pp != '\0')
1441                                 pp++;
1442                         if (*pp != '\0') {
1443                                 *pp = '\0';
1444                                 pp++;
1445                                 while (isspace(*pp) && *pp != '\0')
1446                                         pp++;
1447                                 if (*pp != '\0') {
1448                                         PRINT_ERROR("%s", "Too many arguments");
1449                                         res = -EINVAL;
1450                                         goto out_up_free;
1451                                 }
1452                         }
1453                         rc = scst_proc_rename_acg(acg, p);
1454                         break;
1455                 }
1456                 break;
1457         case SCST_PROC_ACTION_ASSIGN:
1458                 rc = scst_proc_assign_handler(p);
1459                 break;
1460         }
1461
1462         if (rc != 0)
1463                 res = rc;
1464
1465 out_up_free:
1466         mutex_unlock(&scst_mutex);
1467
1468 out_free_resume:
1469         scst_resume_activity();
1470
1471 out_free:
1472         free_page((unsigned long)buffer);
1473
1474 out:
1475         TRACE_EXIT_RES(res);
1476         return res;
1477 }
1478
1479 /* The activity supposed to be suspended and scst_mutex held */
1480 static int scst_proc_assign_handler(char *buf)
1481 {
1482         int res = 0;
1483         char *p = buf, *e, *ee;
1484         unsigned long host, channel = 0, id = 0, lun = 0;
1485         struct scst_device *d, *dev = NULL;
1486         struct scst_dev_type *dt, *handler = NULL;
1487
1488         TRACE_ENTRY();
1489
1490         while (isspace(*p) && *p != '\0')
1491                 p++;
1492
1493         host = simple_strtoul(p, &p, 0);
1494         if ((host == ULONG_MAX) || (*p != ':'))
1495                 goto out_synt_err;
1496         p++;
1497         channel = simple_strtoul(p, &p, 0);
1498         if ((channel == ULONG_MAX) || (*p != ':'))
1499                 goto out_synt_err;
1500         p++;
1501         id = simple_strtoul(p, &p, 0);
1502         if ((channel == ULONG_MAX) || (*p != ':'))
1503                 goto out_synt_err;
1504         p++;
1505         lun = simple_strtoul(p, &p, 0);
1506         if (lun == ULONG_MAX)
1507                 goto out_synt_err;
1508
1509         e = p;
1510         e++;
1511         while (isspace(*e) && *e != '\0')
1512                 e++;
1513         ee = e;
1514         while (!isspace(*ee) && *ee != '\0')
1515                 ee++;
1516         *ee = '\0';
1517
1518         TRACE_DBG("Dev %ld:%ld:%ld:%ld, handler %s", host, channel, id, lun, e);
1519
1520         list_for_each_entry(d, &scst_dev_list, dev_list_entry) {
1521                 if ((d->virt_id == 0) &&
1522                     d->scsi_dev->host->host_no == host &&
1523                     d->scsi_dev->channel == channel &&
1524                     d->scsi_dev->id == id &&
1525                     d->scsi_dev->lun == lun) {
1526                         dev = d;
1527                         TRACE_DBG("Dev %p (%ld:%ld:%ld:%ld) found",
1528                                   dev, host, channel, id, lun);
1529                         break;
1530                 }
1531         }
1532
1533         if (dev == NULL) {
1534                 PRINT_ERROR("Device %ld:%ld:%ld:%ld not found",
1535                                host, channel, id, lun);
1536                 res = -EINVAL;
1537                 goto out;
1538         }
1539
1540         list_for_each_entry(dt, &scst_dev_type_list, dev_type_list_entry) {
1541                 if (!strcmp(dt->name, e)) {
1542                         handler = dt;
1543                         TRACE_DBG("Dev handler %p with name %s found",
1544                                   dt, dt->name);
1545                         break;
1546                 }
1547         }
1548
1549         if (handler == NULL) {
1550                 PRINT_ERROR("Handler %s not found", e);
1551                 res = -EINVAL;
1552                 goto out;
1553         }
1554
1555         if (dev->scsi_dev->type != handler->type) {
1556                 PRINT_ERROR("Type %d of device %s differs from type "
1557                         "%d of dev handler %s", dev->type,
1558                         dev->handler->name, handler->type, handler->name);
1559                 res = -EINVAL;
1560                 goto out;
1561         }
1562
1563         res = scst_assign_dev_handler(dev, handler);
1564
1565 out:
1566         TRACE_EXIT_RES(res);
1567         return res;
1568
1569 out_synt_err:
1570         PRINT_ERROR("Syntax error on %s", p);
1571         res = -EINVAL;
1572         goto out;
1573 }
1574
1575 static ssize_t scst_proc_groups_devices_write(struct file *file,
1576                                         const char __user *buf,
1577                                         size_t length, loff_t *off)
1578 {
1579         int res, action, virt = 0, rc, read_only = 0;
1580         char *buffer, *p, *e = NULL;
1581         unsigned int host, channel = 0, id = 0, lun = 0, virt_lun;
1582         struct scst_acg *acg =
1583                 (struct scst_acg *)PDE(file->f_dentry->d_inode)->data;
1584         struct scst_acg_dev *acg_dev = NULL, *acg_dev_tmp;
1585         struct scst_device *d, *dev = NULL;
1586
1587         TRACE_ENTRY();
1588
1589         if (length > SCST_PROC_BLOCK_SIZE) {
1590                 res = -EOVERFLOW;
1591                 goto out;
1592         }
1593         if (!buf) {
1594                 res = -EINVAL;
1595                 goto out;
1596         }
1597         buffer = (char *)__get_free_page(GFP_KERNEL);
1598         if (!buffer) {
1599                 res = -ENOMEM;
1600                 goto out;
1601         }
1602         if (copy_from_user(buffer, buf, length)) {
1603                 res = -EFAULT;
1604                 goto out_free;
1605         }
1606         if (length < PAGE_SIZE) {
1607                 buffer[length] = '\0';
1608         } else if (buffer[PAGE_SIZE-1]) {
1609                 res = -EINVAL;
1610                 goto out_free;
1611         }
1612
1613         /*
1614          * Usage: echo "add|del H:C:I:L lun [READ_ONLY]" \
1615          *          >/proc/scsi_tgt/groups/GROUP_NAME/devices
1616          *   or   echo "add|del V_NAME lun [READ_ONLY]" \
1617          *          >/proc/scsi_tgt/groups/GROUP_NAME/devices
1618          *   or   echo "clear" >/proc/scsi_tgt/groups/GROUP_NAME/devices
1619          */
1620         p = buffer;
1621         if (p[strlen(p) - 1] == '\n')
1622                 p[strlen(p) - 1] = '\0';
1623         if (!strncasecmp("clear", p, 5)) {
1624                 action = SCST_PROC_ACTION_CLEAR;
1625         } else if (!strncasecmp("add ", p, 4)) {
1626                 p += 4;
1627                 action = SCST_PROC_ACTION_ADD;
1628         } else if (!strncasecmp("del ", p, 4)) {
1629                 p += 4;
1630                 action = SCST_PROC_ACTION_DEL;
1631         } else {
1632                 PRINT_ERROR("Unknown action \"%s\"", p);
1633                 res = -EINVAL;
1634                 goto out_free;
1635         }
1636
1637         res = scst_suspend_activity(true);
1638         if (res != 0)
1639                 goto out_free;
1640
1641         if (mutex_lock_interruptible(&scst_mutex) != 0) {
1642                 res = -EINTR;
1643                 goto out_free_resume;
1644         }
1645
1646         res = length;
1647
1648         switch (action) {
1649         case SCST_PROC_ACTION_ADD:
1650         case SCST_PROC_ACTION_DEL:
1651                 while (isspace(*p) && *p != '\0')
1652                         p++;
1653                 e = p; /* save p */
1654                 host = simple_strtoul(p, &p, 0);
1655                 if (*p == ':') {
1656                         channel = simple_strtoul(p + 1, &p, 0);
1657                         id = simple_strtoul(p + 1, &p, 0);
1658                         lun = simple_strtoul(p + 1, &p, 0);
1659                         e = p;
1660                 } else {
1661                         virt++;
1662                         p = e; /* restore p */
1663                         while (!isspace(*e) && *e != '\0')
1664                                 e++;
1665                         *e = 0;
1666                 }
1667
1668                 list_for_each_entry(d, &scst_dev_list, dev_list_entry) {
1669                         if (virt) {
1670                                 if (d->virt_id && !strcmp(d->virt_name, p)) {
1671                                         dev = d;
1672                                         TRACE_DBG("Virt device %p (%s) found",
1673                                                   dev, p);
1674                                         break;
1675                                 }
1676                         } else {
1677                                 if (d->scsi_dev &&
1678                                     d->scsi_dev->host->host_no == host &&
1679                                     d->scsi_dev->channel == channel &&
1680                                     d->scsi_dev->id == id &&
1681                                     d->scsi_dev->lun == lun) {
1682                                         dev = d;
1683                                         TRACE_DBG("Dev %p (%d:%d:%d:%d) found",
1684                                                   dev, host, channel, id, lun);
1685                                         break;
1686                                 }
1687                         }
1688                 }
1689                 if (dev == NULL) {
1690                         if (virt) {
1691                                 PRINT_ERROR("Virt device %s not found", p);
1692                         } else {
1693                                 PRINT_ERROR("Device %d:%d:%d:%d not found",
1694                                                host, channel, id, lun);
1695                         }
1696                         res = -EINVAL;
1697                         goto out_free_up;
1698                 }
1699                 break;
1700         }
1701
1702         /* ToDo: create separate functions */
1703
1704         switch (action) {
1705         case SCST_PROC_ACTION_ADD:
1706                 e++;
1707                 while (isspace(*e) && *e != '\0')
1708                         e++;
1709                 virt_lun = simple_strtoul(e, &e, 0);
1710
1711                 while (isspace(*e) && *e != '\0')
1712                         e++;
1713
1714                 if (*e != '\0') {
1715                         if (!strncasecmp("READ_ONLY", e, 9))
1716                                 read_only = 1;
1717                         else {
1718                                 PRINT_ERROR("Unknown option \"%s\"", e);
1719                                 res = -EINVAL;
1720                                 goto out_free_up;
1721                         }
1722                 }
1723
1724                 list_for_each_entry(acg_dev_tmp, &acg->acg_dev_list,
1725                                     acg_dev_list_entry) {
1726                         if (acg_dev_tmp->lun == virt_lun) {
1727                                 acg_dev = acg_dev_tmp;
1728                                 break;
1729                         }
1730                 }
1731                 if (acg_dev) {
1732                         acg_dev = acg_dev_tmp;
1733                         PRINT_ERROR("virt lun %d already exists in group %s",
1734                                        virt_lun, acg->acg_name);
1735                         res = -EINVAL;
1736                         goto out_free_up;
1737                 }
1738                 rc = scst_acg_add_dev(acg, dev, virt_lun, read_only);
1739                 if (rc) {
1740                         PRINT_ERROR("scst_acg_add_dev() returned %d", rc);
1741                         res = rc;
1742                 }
1743                 break;
1744         case SCST_PROC_ACTION_DEL:
1745                 rc = scst_acg_remove_dev(acg, dev);
1746                 if (rc) {
1747                         PRINT_ERROR("scst_acg_remove_dev() returned %d", rc);
1748                         res = rc;
1749                 }
1750                 break;
1751         case SCST_PROC_ACTION_CLEAR:
1752                 list_for_each_entry_safe(acg_dev, acg_dev_tmp,
1753                                          &acg->acg_dev_list,
1754                                          acg_dev_list_entry) {
1755                         rc = scst_acg_remove_dev(acg, acg_dev->dev);
1756                         if (rc) {
1757                                 PRINT_ERROR("scst_acg_remove_dev() "
1758                                                "return %d", rc);
1759                                 res = rc;
1760                         }
1761                 }
1762                 break;
1763         }
1764
1765 out_free_up:
1766         mutex_unlock(&scst_mutex);
1767
1768 out_free_resume:
1769         scst_resume_activity();
1770
1771 out_free:
1772         free_page((unsigned long)buffer);
1773
1774 out:
1775         TRACE_EXIT_RES(res);
1776         return res;
1777 }
1778
1779 static ssize_t scst_proc_groups_names_write(struct file *file,
1780                                         const char __user *buf,
1781                                         size_t length, loff_t *off)
1782 {
1783         int res = length, rc = 0, action;
1784         char *buffer, *p, *pp = NULL;
1785         struct scst_acg *acg =
1786                 (struct scst_acg *)PDE(file->f_dentry->d_inode)->data;
1787         struct scst_acn *n, *nn;
1788
1789         TRACE_ENTRY();
1790
1791         if (length > SCST_PROC_BLOCK_SIZE) {
1792                 res = -EOVERFLOW;
1793                 goto out;
1794         }
1795         if (!buf) {
1796                 res = -EINVAL;
1797                 goto out;
1798         }
1799         buffer = (char *)__get_free_page(GFP_KERNEL);
1800         if (!buffer) {
1801                 res = -ENOMEM;
1802                 goto out;
1803         }
1804         if (copy_from_user(buffer, buf, length)) {
1805                 res = -EFAULT;
1806                 goto out_free;
1807         }
1808         if (length < PAGE_SIZE) {
1809                 buffer[length] = '\0';
1810         } else if (buffer[PAGE_SIZE-1]) {
1811                 res = -EINVAL;
1812                 goto out_free;
1813         }
1814
1815         /*
1816          * Usage: echo "add|del NAME" >/proc/scsi_tgt/groups/GROUP_NAME/names
1817          *   or   echo "move NAME NEW_GROUP_NAME" >/proc/scsi_tgt/groups/OLD_GROUP_NAME/names"
1818          *   or   echo "clear" >/proc/scsi_tgt/groups/GROUP_NAME/names
1819          */
1820         p = buffer;
1821         if (p[strlen(p) - 1] == '\n')
1822                 p[strlen(p) - 1] = '\0';
1823         if (!strncasecmp("clear", p, 5)) {
1824                 action = SCST_PROC_ACTION_CLEAR;
1825         } else if (!strncasecmp("add ", p, 4)) {
1826                 p += 4;
1827                 action = SCST_PROC_ACTION_ADD;
1828         } else if (!strncasecmp("del ", p, 4)) {
1829                 p += 4;
1830                 action = SCST_PROC_ACTION_DEL;
1831         } else if (!strncasecmp("move ", p, 5)) {
1832                 p += 5;
1833                 action = SCST_PROC_ACTION_MOVE;
1834         } else {
1835                 PRINT_ERROR("Unknown action \"%s\"", p);
1836                 res = -EINVAL;
1837                 goto out_free;
1838         }
1839
1840         switch (action) {
1841         case SCST_PROC_ACTION_ADD:
1842         case SCST_PROC_ACTION_DEL:
1843         case SCST_PROC_ACTION_MOVE:
1844                 while (isspace(*p) && *p != '\0')
1845                         p++;
1846                 pp = p;
1847                 while (!isspace(*pp) && *pp != '\0')
1848                         pp++;
1849                 if (*pp != '\0') {
1850                         *pp = '\0';
1851                         pp++;
1852                         while (isspace(*pp) && *pp != '\0')
1853                                 pp++;
1854                         if (*pp != '\0') {
1855                                 switch (action) {
1856                                 case SCST_PROC_ACTION_ADD:
1857                                 case SCST_PROC_ACTION_DEL:
1858                                         PRINT_ERROR("%s", "Too many "
1859                                                 "arguments");
1860                                         res = -EINVAL;
1861                                         goto out_free;
1862                                 }
1863                         }
1864                 }
1865                 break;
1866         }
1867
1868         rc = scst_suspend_activity(true);
1869         if (rc != 0)
1870                 goto out_free;
1871
1872         if (mutex_lock_interruptible(&scst_mutex) != 0) {
1873                 res = -EINTR;
1874                 goto out_free_resume;
1875         }
1876
1877         switch (action) {
1878         case SCST_PROC_ACTION_ADD:
1879                 rc = scst_acg_add_name(acg, p);
1880                 break;
1881         case SCST_PROC_ACTION_DEL:
1882                 rc = scst_acg_remove_name(acg, p, true);
1883                 break;
1884         case SCST_PROC_ACTION_MOVE:
1885         {
1886                 struct scst_acg *a, *new_acg = NULL;
1887                 char *name = p;
1888                 p = pp;
1889                 while (!isspace(*pp) && *pp != '\0')
1890                         pp++;
1891                 if (*pp != '\0') {
1892                         *pp = '\0';
1893                         pp++;
1894                         while (isspace(*pp) && *pp != '\0')
1895                                 pp++;
1896                         if (*pp != '\0') {
1897                                 PRINT_ERROR("%s", "Too many arguments");
1898                                 res = -EINVAL;
1899                                 goto out_free_unlock;
1900                         }
1901                 }
1902                 list_for_each_entry(a, &scst_acg_list, scst_acg_list_entry) {
1903                         if (strcmp(a->acg_name, p) == 0) {
1904                                 TRACE_DBG("group (acg) %p %s found",
1905                                           a, a->acg_name);
1906                                 new_acg = a;
1907                                 break;
1908                         }
1909                 }
1910                 if (new_acg == NULL) {
1911                         PRINT_ERROR("Group %s not found", p);
1912                         res = -EINVAL;
1913                         goto out_free_unlock;
1914                 }
1915                 rc = scst_acg_remove_name(acg, name, false);
1916                 if (rc != 0)
1917                         goto out_free_unlock;
1918                 rc = scst_acg_add_name(new_acg, name);
1919                 break;
1920         }
1921         case SCST_PROC_ACTION_CLEAR:
1922                 list_for_each_entry_safe(n, nn, &acg->acn_list,
1923                                          acn_list_entry) {
1924                         __scst_acg_remove_acn(n);
1925                 }
1926                 scst_check_reassign_sessions();
1927                 break;
1928         }
1929
1930 out_free_unlock:
1931         mutex_unlock(&scst_mutex);
1932
1933 out_free_resume:
1934         scst_resume_activity();
1935
1936 out_free:
1937         free_page((unsigned long)buffer);
1938
1939 out:
1940         if (rc < 0)
1941                 res = rc;
1942
1943         TRACE_EXIT_RES(res);
1944         return res;
1945 }
1946
1947 static int scst_version_info_show(struct seq_file *seq, void *v)
1948 {
1949         TRACE_ENTRY();
1950
1951         seq_printf(seq, "%s\n", SCST_VERSION_STRING);
1952
1953 #ifdef CONFIG_SCST_STRICT_SERIALIZING
1954         seq_printf(seq, "Strict serializing enabled\n");
1955 #endif
1956
1957 #ifdef CONFIG_SCST_EXTRACHECKS
1958         seq_printf(seq, "EXTRACHECKS\n");
1959 #endif
1960
1961 #ifdef CONFIG_SCST_TRACING
1962         seq_printf(seq, "TRACING\n");
1963 #endif
1964
1965 #ifdef CONFIG_SCST_DEBUG
1966         seq_printf(seq, "DEBUG\n");
1967 #endif
1968
1969 #ifdef CONFIG_SCST_DEBUG_TM
1970         seq_printf(seq, "DEBUG_TM\n");
1971 #endif
1972
1973 #ifdef CONFIG_SCST_DEBUG_RETRY
1974         seq_printf(seq, "DEBUG_RETRY\n");
1975 #endif
1976
1977 #ifdef CONFIG_SCST_DEBUG_OOM
1978         seq_printf(seq, "DEBUG_OOM\n");
1979 #endif
1980
1981 #ifdef CONFIG_SCST_DEBUG_SN
1982         seq_printf(seq, "DEBUG_SN\n");
1983 #endif
1984
1985 #ifdef CONFIG_SCST_USE_EXPECTED_VALUES
1986         seq_printf(seq, "USE_EXPECTED_VALUES\n");
1987 #endif
1988
1989 #ifdef CONFIG_SCST_ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ
1990         seq_printf(seq, "ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ\n");
1991 #endif
1992
1993 #ifdef CONFIG_SCST_STRICT_SECURITY
1994         seq_printf(seq, "SCST_STRICT_SECURITY\n");
1995 #endif
1996
1997         TRACE_EXIT();
1998         return 0;
1999 }
2000
2001 static struct scst_proc_data scst_version_proc_data = {
2002         SCST_DEF_RW_SEQ_OP(NULL)
2003         .show = scst_version_info_show,
2004 };
2005
2006 static int scst_help_info_show(struct seq_file *seq, void *v)
2007 {
2008         TRACE_ENTRY();
2009
2010         seq_printf(seq, "%s\n", scst_proc_help_string);
2011
2012         TRACE_EXIT();
2013         return 0;
2014 }
2015
2016 static struct scst_proc_data scst_help_proc_data = {
2017         SCST_DEF_RW_SEQ_OP(NULL)
2018         .show = scst_help_info_show,
2019 };
2020
2021 static int scst_dev_handler_type_info_show(struct seq_file *seq, void *v)
2022 {
2023         struct scst_dev_type *dev_type = (struct scst_dev_type *)seq->private;
2024
2025         TRACE_ENTRY();
2026
2027         seq_printf(seq, "%d - %s\n", dev_type->type,
2028                    dev_type->type > (int)ARRAY_SIZE(scst_proc_dev_handler_type)
2029                    ? "unknown" : scst_proc_dev_handler_type[dev_type->type]);
2030
2031         TRACE_EXIT();
2032         return 0;
2033 }
2034
2035 static struct scst_proc_data scst_dev_handler_type_proc_data = {
2036         SCST_DEF_RW_SEQ_OP(NULL)
2037         .show = scst_dev_handler_type_info_show,
2038 };
2039
2040 static int scst_sessions_info_show(struct seq_file *seq, void *v)
2041 {
2042         int res = 0;
2043         struct scst_acg *acg;
2044         struct scst_session *sess;
2045
2046         TRACE_ENTRY();
2047
2048         if (mutex_lock_interruptible(&scst_mutex) != 0) {
2049                 res = -EINTR;
2050                 goto out;
2051         }
2052
2053         seq_printf(seq, "%-20s %-45s %-35s %-15s\n",
2054                    "Target name", "Initiator name",
2055                    "Group name", "Command Count");
2056
2057         list_for_each_entry(acg, &scst_acg_list, scst_acg_list_entry) {
2058                 list_for_each_entry(sess, &acg->acg_sess_list,
2059                         acg_sess_list_entry) {
2060                         seq_printf(seq, "%-20s %-45s %-35s %-15d\n",
2061                                         sess->tgt->tgtt->name,
2062                                         sess->initiator_name,
2063                                         acg->acg_name,
2064                                         atomic_read(&sess->sess_cmd_count));
2065                 }
2066         }
2067
2068         mutex_unlock(&scst_mutex);
2069
2070 out:
2071         TRACE_EXIT_RES(res);
2072         return res;
2073 }
2074
2075 static struct scst_proc_data scst_sessions_proc_data = {
2076         SCST_DEF_RW_SEQ_OP(NULL)
2077         .show = scst_sessions_info_show,
2078 };
2079
2080
2081 static struct scst_proc_data scst_sgv_proc_data = {
2082         SCST_DEF_RW_SEQ_OP(NULL)
2083         .show = sgv_procinfo_show,
2084 };
2085
2086 static int scst_groups_names_show(struct seq_file *seq, void *v)
2087 {
2088         int res = 0;
2089         struct scst_acg *acg = (struct scst_acg *)seq->private;
2090         struct scst_acn *name;
2091
2092         TRACE_ENTRY();
2093
2094         if (mutex_lock_interruptible(&scst_mutex) != 0) {
2095                 res = -EINTR;
2096                 goto out;
2097         }
2098
2099         list_for_each_entry(name, &acg->acn_list, acn_list_entry) {
2100                 seq_printf(seq, "%s\n", name->name);
2101         }
2102
2103         mutex_unlock(&scst_mutex);
2104
2105 out:
2106         TRACE_EXIT_RES(res);
2107         return res;
2108 }
2109
2110 static struct scst_proc_data scst_groups_names_proc_data = {
2111         SCST_DEF_RW_SEQ_OP(scst_proc_groups_names_write)
2112         .show = scst_groups_names_show,
2113 };
2114
2115 static int scst_groups_devices_show(struct seq_file *seq, void *v)
2116 {
2117         int res = 0;
2118         struct scst_acg *acg = (struct scst_acg *)seq->private;
2119         struct scst_acg_dev *acg_dev;
2120
2121         TRACE_ENTRY();
2122
2123         if (mutex_lock_interruptible(&scst_mutex) != 0) {
2124                 res = -EINTR;
2125                 goto out;
2126         }
2127
2128         seq_printf(seq, "%-60s%-13s%s\n", "Device (host:ch:id:lun or name)",
2129                        "LUN", "Options");
2130
2131         list_for_each_entry(acg_dev, &acg->acg_dev_list, acg_dev_list_entry) {
2132                 if (acg_dev->dev->virt_id == 0) {
2133                         char conv[60];
2134                         int size = sizeof(conv);
2135
2136                         memset(conv, 0, size);
2137                         size = snprintf(conv, size, "%d:%d:%d:",
2138                                         acg_dev->dev->scsi_dev->host->host_no,
2139                                         acg_dev->dev->scsi_dev->channel,
2140                                         acg_dev->dev->scsi_dev->id);
2141                         seq_printf(seq, "%s", conv);
2142
2143                         /*
2144                          * For some reason the third string argument always
2145                          * shown as NULL, so we have to split it on 2 calls.
2146                          */
2147                         sprintf(conv, "%%-%dd%%-13d", 60 - size);
2148                         size += seq_printf(seq, conv,
2149                                         acg_dev->dev->scsi_dev->lun,
2150                                         acg_dev->lun);
2151                         seq_printf(seq, "%s\n",
2152                                 acg_dev->rd_only ? "RO" : "");
2153                 } else {
2154                         seq_printf(seq, "%-60s%-13lld%s\n",
2155                                        acg_dev->dev->virt_name,
2156                                        (long long unsigned int)acg_dev->lun,
2157                                        acg_dev->rd_only ? "RO" : "");
2158                 }
2159         }
2160         mutex_unlock(&scst_mutex);
2161
2162 out:
2163         TRACE_EXIT_RES(res);
2164         return res;
2165 }
2166
2167 static struct scst_proc_data scst_groups_devices_proc_data = {
2168         SCST_DEF_RW_SEQ_OP(scst_proc_groups_devices_write)
2169         .show = scst_groups_devices_show,
2170 };
2171
2172 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
2173
2174 static int scst_proc_read_tlb(const struct scst_proc_log *tbl,
2175                               struct seq_file *seq,
2176         unsigned long log_level, int *first)
2177 {
2178         const struct scst_proc_log *t = tbl;
2179         int res = 0;
2180
2181         while (t->token) {
2182                 if (log_level & t->val) {
2183                         seq_printf(seq, "%s%s", *first ? "" : " | ", t->token);
2184                         *first = 0;
2185                 }
2186                 t++;
2187         }
2188         return res;
2189 }
2190
2191 int scst_proc_log_entry_read(struct seq_file *seq, unsigned long log_level,
2192                              const struct scst_proc_log *tbl)
2193 {
2194         int res = 0, first = 1;
2195
2196         TRACE_ENTRY();
2197
2198         scst_proc_read_tlb(scst_proc_trace_tbl, seq, log_level, &first);
2199
2200         if (tbl)
2201                 scst_proc_read_tlb(tbl, seq, log_level, &first);
2202
2203         seq_printf(seq, "%s\n", first ? "none" : "");
2204
2205         TRACE_EXIT_RES(res);
2206         return res;
2207 }
2208 EXPORT_SYMBOL(scst_proc_log_entry_read);
2209
2210 static int log_info_show(struct seq_file *seq, void *v)
2211 {
2212         int res;
2213
2214         TRACE_ENTRY();
2215
2216         if (mutex_lock_interruptible(&scst_log_mutex) != 0) {
2217                 res = -EINTR;
2218                 goto out;
2219         }
2220
2221         res = scst_proc_log_entry_read(seq, trace_flag,
2222                                        scst_proc_local_trace_tbl);
2223
2224         mutex_unlock(&scst_log_mutex);
2225
2226 out:
2227         TRACE_EXIT_RES(res);
2228         return res;
2229 }
2230
2231 static struct scst_proc_data scst_log_proc_data = {
2232         SCST_DEF_RW_SEQ_OP(scst_proc_scsi_tgt_gen_write_log)
2233         .show = log_info_show,
2234         .data = "scsi_tgt",
2235 };
2236
2237 #endif
2238
2239 static int scst_tgt_info_show(struct seq_file *seq, void *v)
2240 {
2241         int res = 0;
2242         struct scst_device *dev;
2243
2244         TRACE_ENTRY();
2245
2246         if (mutex_lock_interruptible(&scst_mutex) != 0) {
2247                 res = -EINTR;
2248                 goto out;
2249         }
2250
2251         seq_printf(seq, "%-60s%s\n", "Device (host:ch:id:lun or name)",
2252                    "Device handler");
2253         list_for_each_entry(dev, &scst_dev_list, dev_list_entry) {
2254                 if (dev->virt_id == 0) {
2255                         char conv[60];
2256                         int size = sizeof(conv);
2257                         size = snprintf(conv, size, "%d:%d:%d:",
2258                                         dev->scsi_dev->host->host_no,
2259                                         dev->scsi_dev->channel,
2260                                         dev->scsi_dev->id);
2261                         seq_printf(seq, "%s", conv);
2262                         sprintf(conv, "%%-%dd%%s\n", 60 - size);
2263                         seq_printf(seq, conv, dev->scsi_dev->lun,
2264                                    dev->handler ? dev->handler->name : "-");
2265                 } else
2266                         seq_printf(seq, "%-60s%s\n",
2267                                    dev->virt_name, dev->handler->name);
2268         }
2269
2270         mutex_unlock(&scst_mutex);
2271
2272 out:
2273         TRACE_EXIT_RES(res);
2274         return res;
2275 }
2276
2277 static struct scst_proc_data scst_tgt_proc_data = {
2278         SCST_DEF_RW_SEQ_OP(scst_proc_scsi_tgt_gen_write)
2279         .show = scst_tgt_info_show,
2280 };
2281
2282 static int scst_threads_info_show(struct seq_file *seq, void *v)
2283 {
2284         TRACE_ENTRY();
2285
2286         seq_printf(seq, "%d\n", scst_global_threads_count());
2287
2288         TRACE_EXIT();
2289         return 0;
2290 }
2291
2292 static struct scst_proc_data scst_threads_proc_data = {
2293         SCST_DEF_RW_SEQ_OP(scst_proc_threads_write)
2294         .show = scst_threads_info_show,
2295 };
2296
2297 static int scst_scsi_tgtinfo_show(struct seq_file *seq, void *v)
2298 {
2299         struct scst_tgt *vtt = seq->private;
2300         int res = 0;
2301
2302         TRACE_ENTRY();
2303
2304         if (mutex_lock_interruptible(&scst_proc_mutex) != 0) {
2305                 res = -EINTR;
2306                 goto out;
2307         }
2308
2309         if (vtt->tgtt->read_proc)
2310                 res = vtt->tgtt->read_proc(seq, vtt);
2311
2312         mutex_unlock(&scst_proc_mutex);
2313 out:
2314         TRACE_EXIT_RES(res);
2315         return res;
2316 }
2317
2318 static struct scst_proc_data scst_scsi_tgt_proc_data = {
2319         SCST_DEF_RW_SEQ_OP(scst_proc_scsi_tgt_write)
2320         .show = scst_scsi_tgtinfo_show,
2321 };
2322
2323 static int scst_dev_handler_info_show(struct seq_file *seq, void *v)
2324 {
2325         struct scst_dev_type *dev_type = seq->private;
2326         int res = 0;
2327
2328         TRACE_ENTRY();
2329
2330         if (mutex_lock_interruptible(&scst_proc_mutex) != 0) {
2331                 res = -EINTR;
2332                 goto out;
2333         }
2334
2335         if (dev_type->read_proc)
2336                 res = dev_type->read_proc(seq, dev_type);
2337
2338         mutex_unlock(&scst_proc_mutex);
2339
2340 out:
2341         TRACE_EXIT_RES(res);
2342         return res;
2343 }
2344
2345 static struct scst_proc_data scst_dev_handler_proc_data = {
2346         SCST_DEF_RW_SEQ_OP(scst_proc_scsi_dev_handler_write)
2347         .show = scst_dev_handler_info_show,
2348 };
2349
2350 struct proc_dir_entry *scst_create_proc_entry(struct proc_dir_entry *root,
2351         const char *name, struct scst_proc_data *pdata)
2352 {
2353         struct proc_dir_entry *p = NULL;
2354
2355         TRACE_ENTRY();
2356
2357         if (root) {
2358                 mode_t mode;
2359
2360                 mode = S_IFREG | S_IRUGO | (pdata->seq_op.write ? S_IWUSR : 0);
2361                 p = create_proc_entry(name, mode, root);
2362                 if (p == NULL) {
2363                         PRINT_ERROR("Fail to create entry %s in /proc", name);
2364                 } else {
2365                         p->proc_fops = &pdata->seq_op;
2366                         p->data = pdata->data;
2367                 }
2368         }
2369
2370         TRACE_EXIT();
2371         return p;
2372 }
2373 EXPORT_SYMBOL(scst_create_proc_entry);
2374
2375 int scst_single_seq_open(struct inode *inode, struct file *file)
2376 {
2377 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
2378         struct scst_proc_data *pdata = container_of(PDE(inode)->proc_fops,
2379                 struct scst_proc_data, seq_op);
2380 #else
2381         struct scst_proc_data *pdata = container_of(inode->i_fop,
2382                 struct scst_proc_data, seq_op);
2383 #endif
2384         return single_open(file, pdata->show, PDE(inode)->data);
2385 }
2386 EXPORT_SYMBOL(scst_single_seq_open);