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