e3c2b2b0c380c8b1916e539b20ad7962faf6260d
[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, scst_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, scst_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(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         const char *name;
913         struct proc_dir_entry *acg_proc_root = acg->acg_proc_root;
914         int res = 0;
915
916         TRACE_ENTRY();
917
918         if (acg != scst_default_acg) {
919                 name = acg->acg_name;
920                 res = scst_destroy_acg(acg);
921                 if (res == 0) {
922                         if (remove_proc)
923                                 scst_proc_del_acg_tree(acg_proc_root, name);
924                         kfree(name);
925                 }
926         }
927
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                                  scst_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, scst_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, scst_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                 break;
2193         }
2194         case SCST_PROC_ACTION_CLEAR:
2195                 list_for_each_entry_safe(n, nn, &acg->acn_list,
2196                                          acn_list_entry) {
2197                         __scst_acg_remove_acn(n);
2198                 }
2199                 scst_check_reassign_sessions();
2200                 break;
2201         }
2202
2203 out_free_unlock:
2204         mutex_unlock(&scst_mutex);
2205
2206 out_free_resume:
2207         scst_resume_activity();
2208
2209 out_free:
2210         free_page((unsigned long)buffer);
2211
2212 out:
2213         if (rc < 0)
2214                 res = rc;
2215
2216         TRACE_EXIT_RES(res);
2217         return res;
2218 }
2219
2220 static int scst_version_info_show(struct seq_file *seq, void *v)
2221 {
2222         TRACE_ENTRY();
2223
2224         seq_printf(seq, "%s\n", SCST_VERSION_STRING);
2225
2226 #ifdef CONFIG_SCST_STRICT_SERIALIZING
2227         seq_printf(seq, "Strict serializing enabled\n");
2228 #endif
2229
2230 #ifdef CONFIG_SCST_EXTRACHECKS
2231         seq_printf(seq, "EXTRACHECKS\n");
2232 #endif
2233
2234 #ifdef CONFIG_SCST_TRACING
2235         seq_printf(seq, "TRACING\n");
2236 #endif
2237
2238 #ifdef CONFIG_SCST_DEBUG
2239         seq_printf(seq, "DEBUG\n");
2240 #endif
2241
2242 #ifdef CONFIG_SCST_DEBUG_TM
2243         seq_printf(seq, "DEBUG_TM\n");
2244 #endif
2245
2246 #ifdef CONFIG_SCST_DEBUG_RETRY
2247         seq_printf(seq, "DEBUG_RETRY\n");
2248 #endif
2249
2250 #ifdef CONFIG_SCST_DEBUG_OOM
2251         seq_printf(seq, "DEBUG_OOM\n");
2252 #endif
2253
2254 #ifdef CONFIG_SCST_DEBUG_SN
2255         seq_printf(seq, "DEBUG_SN\n");
2256 #endif
2257
2258 #ifdef CONFIG_SCST_USE_EXPECTED_VALUES
2259         seq_printf(seq, "USE_EXPECTED_VALUES\n");
2260 #endif
2261
2262 #ifdef CONFIG_SCST_ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ
2263         seq_printf(seq, "ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ\n");
2264 #endif
2265
2266 #ifdef CONFIG_SCST_STRICT_SECURITY
2267         seq_printf(seq, "SCST_STRICT_SECURITY\n");
2268 #endif
2269
2270         TRACE_EXIT();
2271         return 0;
2272 }
2273
2274 static struct scst_proc_data scst_version_proc_data = {
2275         SCST_DEF_RW_SEQ_OP(NULL)
2276         .show = scst_version_info_show,
2277 };
2278
2279 static int scst_help_info_show(struct seq_file *seq, void *v)
2280 {
2281         TRACE_ENTRY();
2282
2283         seq_printf(seq, "%s\n", scst_proc_help_string);
2284
2285         TRACE_EXIT();
2286         return 0;
2287 }
2288
2289 static struct scst_proc_data scst_help_proc_data = {
2290         SCST_DEF_RW_SEQ_OP(NULL)
2291         .show = scst_help_info_show,
2292 };
2293
2294 static int scst_dev_handler_type_info_show(struct seq_file *seq, void *v)
2295 {
2296         struct scst_dev_type *dev_type = (struct scst_dev_type *)seq->private;
2297
2298         TRACE_ENTRY();
2299
2300         seq_printf(seq, "%d - %s\n", dev_type->type,
2301                    dev_type->type > (int)ARRAY_SIZE(scst_proc_dev_handler_type)
2302                    ? "unknown" : scst_proc_dev_handler_type[dev_type->type]);
2303
2304         TRACE_EXIT();
2305         return 0;
2306 }
2307
2308 static struct scst_proc_data scst_dev_handler_type_proc_data = {
2309         SCST_DEF_RW_SEQ_OP(NULL)
2310         .show = scst_dev_handler_type_info_show,
2311 };
2312
2313 static int scst_sessions_info_show(struct seq_file *seq, void *v)
2314 {
2315         int res = 0;
2316         struct scst_acg *acg;
2317         struct scst_session *sess;
2318
2319         TRACE_ENTRY();
2320
2321         if (mutex_lock_interruptible(&scst_mutex) != 0) {
2322                 res = -EINTR;
2323                 goto out;
2324         }
2325
2326         seq_printf(seq, "%-20s %-45s %-35s %-15s\n",
2327                    "Target name", "Initiator name",
2328                    "Group name", "Active/All Commands Count");
2329
2330         list_for_each_entry(acg, &scst_acg_list, scst_acg_list_entry) {
2331                 list_for_each_entry(sess, &acg->acg_sess_list,
2332                                 acg_sess_list_entry) {
2333                         int active_cmds = 0, t;
2334                         for (t = TGT_DEV_HASH_SIZE-1; t >= 0; t--) {
2335                                 struct list_head *sess_tgt_dev_list_head =
2336                                         &sess->sess_tgt_dev_list_hash[t];
2337                                 struct scst_tgt_dev *tgt_dev;
2338                                 list_for_each_entry(tgt_dev,
2339                                                 sess_tgt_dev_list_head,
2340                                                 sess_tgt_dev_list_entry) {
2341                                         active_cmds += atomic_read(&tgt_dev->tgt_dev_cmd_count);
2342                                 }
2343                         }
2344                         seq_printf(seq, "%-20s %-45s %-35s %d/%d\n",
2345                                         sess->tgt->tgtt->name,
2346                                         sess->initiator_name,
2347                                         acg->acg_name, active_cmds,
2348                                         atomic_read(&sess->sess_cmd_count));
2349                 }
2350         }
2351
2352         mutex_unlock(&scst_mutex);
2353
2354 out:
2355         TRACE_EXIT_RES(res);
2356         return res;
2357 }
2358
2359 static struct scst_proc_data scst_sessions_proc_data = {
2360         SCST_DEF_RW_SEQ_OP(NULL)
2361         .show = scst_sessions_info_show,
2362 };
2363
2364 static struct scst_proc_data scst_sgv_proc_data = {
2365         SCST_DEF_RW_SEQ_OP(NULL)
2366         .show = sgv_procinfo_show,
2367 };
2368
2369 static int scst_groups_names_show(struct seq_file *seq, void *v)
2370 {
2371         int res = 0;
2372         struct scst_acg *acg = (struct scst_acg *)seq->private;
2373         struct scst_acn *name;
2374
2375         TRACE_ENTRY();
2376
2377         if (mutex_lock_interruptible(&scst_mutex) != 0) {
2378                 res = -EINTR;
2379                 goto out;
2380         }
2381
2382         list_for_each_entry(name, &acg->acn_list, acn_list_entry) {
2383                 seq_printf(seq, "%s\n", name->name);
2384         }
2385
2386         mutex_unlock(&scst_mutex);
2387
2388 out:
2389         TRACE_EXIT_RES(res);
2390         return res;
2391 }
2392
2393 static struct scst_proc_data scst_groups_names_proc_data = {
2394         SCST_DEF_RW_SEQ_OP(scst_proc_groups_names_write)
2395         .show = scst_groups_names_show,
2396 };
2397
2398 static int scst_groups_devices_show(struct seq_file *seq, void *v)
2399 {
2400         int res = 0;
2401         struct scst_acg *acg = (struct scst_acg *)seq->private;
2402         struct scst_acg_dev *acg_dev;
2403
2404         TRACE_ENTRY();
2405
2406         if (mutex_lock_interruptible(&scst_mutex) != 0) {
2407                 res = -EINTR;
2408                 goto out;
2409         }
2410
2411         seq_printf(seq, "%-60s%-13s%s\n", "Device (host:ch:id:lun or name)",
2412                        "LUN", "Options");
2413
2414         list_for_each_entry(acg_dev, &acg->acg_dev_list, acg_dev_list_entry) {
2415                 if (acg_dev->dev->virt_id == 0) {
2416                         char conv[60];
2417                         int size = sizeof(conv);
2418
2419                         memset(conv, 0, size);
2420                         size = snprintf(conv, size, "%d:%d:%d:",
2421                                         acg_dev->dev->scsi_dev->host->host_no,
2422                                         acg_dev->dev->scsi_dev->channel,
2423                                         acg_dev->dev->scsi_dev->id);
2424                         seq_printf(seq, "%s", conv);
2425
2426                         /*
2427                          * For some reason the third string argument always
2428                          * shown as NULL, so we have to split it on 2 calls.
2429                          */
2430                         sprintf(conv, "%%-%dd%%-13d", 60 - size);
2431                         size += seq_printf(seq, conv,
2432                                         acg_dev->dev->scsi_dev->lun,
2433                                         acg_dev->lun);
2434                         seq_printf(seq, "%s\n",
2435                                 acg_dev->rd_only ? "RO" : "");
2436                 } else {
2437                         seq_printf(seq, "%-60s%-13lld%s\n",
2438                                        acg_dev->dev->virt_name,
2439                                        (long long unsigned int)acg_dev->lun,
2440                                        acg_dev->rd_only ? "RO" : "");
2441                 }
2442         }
2443         mutex_unlock(&scst_mutex);
2444
2445 out:
2446         TRACE_EXIT_RES(res);
2447         return res;
2448 }
2449
2450 static struct scst_proc_data scst_groups_devices_proc_data = {
2451         SCST_DEF_RW_SEQ_OP(scst_proc_groups_devices_write)
2452         .show = scst_groups_devices_show,
2453 };
2454
2455 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
2456
2457 static int scst_proc_read_tlb(const struct scst_trace_log *tbl,
2458                               struct seq_file *seq,
2459         unsigned long log_level, int *first)
2460 {
2461         const struct scst_trace_log *t = tbl;
2462         int res = 0;
2463
2464         while (t->token) {
2465                 if (log_level & t->val) {
2466                         seq_printf(seq, "%s%s", *first ? "" : " | ", t->token);
2467                         *first = 0;
2468                 }
2469                 t++;
2470         }
2471         return res;
2472 }
2473
2474 int scst_proc_log_entry_read(struct seq_file *seq, unsigned long log_level,
2475                              const struct scst_trace_log *tbl)
2476 {
2477         int res = 0, first = 1;
2478
2479         TRACE_ENTRY();
2480
2481         scst_proc_read_tlb(scst_proc_trace_tbl, seq, log_level, &first);
2482
2483         if (tbl)
2484                 scst_proc_read_tlb(tbl, seq, log_level, &first);
2485
2486         seq_printf(seq, "%s\n", first ? "none" : "");
2487
2488         TRACE_EXIT_RES(res);
2489         return res;
2490 }
2491 EXPORT_SYMBOL(scst_proc_log_entry_read);
2492
2493 static int log_info_show(struct seq_file *seq, void *v)
2494 {
2495         int res;
2496
2497         TRACE_ENTRY();
2498
2499         if (mutex_lock_interruptible(&scst_log_mutex) != 0) {
2500                 res = -EINTR;
2501                 goto out;
2502         }
2503
2504         res = scst_proc_log_entry_read(seq, trace_flag,
2505                                        scst_proc_local_trace_tbl);
2506
2507         mutex_unlock(&scst_log_mutex);
2508
2509 out:
2510         TRACE_EXIT_RES(res);
2511         return res;
2512 }
2513
2514 static struct scst_proc_data scst_log_proc_data = {
2515         SCST_DEF_RW_SEQ_OP(scst_proc_scsi_tgt_gen_write_log)
2516         .show = log_info_show,
2517         .data = "scsi_tgt",
2518 };
2519
2520 #endif
2521
2522 static int scst_tgt_info_show(struct seq_file *seq, void *v)
2523 {
2524         int res = 0;
2525         struct scst_device *dev;
2526
2527         TRACE_ENTRY();
2528
2529         if (mutex_lock_interruptible(&scst_mutex) != 0) {
2530                 res = -EINTR;
2531                 goto out;
2532         }
2533
2534         seq_printf(seq, "%-60s%s\n", "Device (host:ch:id:lun or name)",
2535                    "Device handler");
2536         list_for_each_entry(dev, &scst_dev_list, dev_list_entry) {
2537                 if (dev->virt_id == 0) {
2538                         char conv[60];
2539                         int size = sizeof(conv);
2540                         size = snprintf(conv, size, "%d:%d:%d:",
2541                                         dev->scsi_dev->host->host_no,
2542                                         dev->scsi_dev->channel,
2543                                         dev->scsi_dev->id);
2544                         seq_printf(seq, "%s", conv);
2545                         sprintf(conv, "%%-%dd%%s\n", 60 - size);
2546                         seq_printf(seq, conv, dev->scsi_dev->lun,
2547                                    dev->handler ? dev->handler->name : "-");
2548                 } else
2549                         seq_printf(seq, "%-60s%s\n",
2550                                    dev->virt_name, dev->handler->name);
2551         }
2552
2553         mutex_unlock(&scst_mutex);
2554
2555 out:
2556         TRACE_EXIT_RES(res);
2557         return res;
2558 }
2559
2560 static struct scst_proc_data scst_tgt_proc_data = {
2561         SCST_DEF_RW_SEQ_OP(scst_proc_scsi_tgt_gen_write)
2562         .show = scst_tgt_info_show,
2563 };
2564
2565 static int scst_threads_info_show(struct seq_file *seq, void *v)
2566 {
2567         TRACE_ENTRY();
2568
2569         seq_printf(seq, "%d\n", scst_global_threads_count());
2570
2571         TRACE_EXIT();
2572         return 0;
2573 }
2574
2575 static struct scst_proc_data scst_threads_proc_data = {
2576         SCST_DEF_RW_SEQ_OP(scst_proc_threads_write)
2577         .show = scst_threads_info_show,
2578 };
2579
2580 static int scst_scsi_tgtinfo_show(struct seq_file *seq, void *v)
2581 {
2582         struct scst_tgt *vtt = seq->private;
2583         int res = 0;
2584
2585         TRACE_ENTRY();
2586
2587         if (mutex_lock_interruptible(&scst_proc_mutex) != 0) {
2588                 res = -EINTR;
2589                 goto out;
2590         }
2591
2592         if (vtt->tgtt->read_proc)
2593                 res = vtt->tgtt->read_proc(seq, vtt);
2594
2595         mutex_unlock(&scst_proc_mutex);
2596 out:
2597         TRACE_EXIT_RES(res);
2598         return res;
2599 }
2600
2601 static struct scst_proc_data scst_scsi_tgt_proc_data = {
2602         SCST_DEF_RW_SEQ_OP(scst_proc_scsi_tgt_write)
2603         .show = scst_scsi_tgtinfo_show,
2604 };
2605
2606 static int scst_dev_handler_info_show(struct seq_file *seq, void *v)
2607 {
2608         struct scst_dev_type *dev_type = seq->private;
2609         int res = 0;
2610
2611         TRACE_ENTRY();
2612
2613         if (mutex_lock_interruptible(&scst_proc_mutex) != 0) {
2614                 res = -EINTR;
2615                 goto out;
2616         }
2617
2618         if (dev_type->read_proc)
2619                 res = dev_type->read_proc(seq, dev_type);
2620
2621         mutex_unlock(&scst_proc_mutex);
2622
2623 out:
2624         TRACE_EXIT_RES(res);
2625         return res;
2626 }
2627
2628 static struct scst_proc_data scst_dev_handler_proc_data = {
2629         SCST_DEF_RW_SEQ_OP(scst_proc_scsi_dev_handler_write)
2630         .show = scst_dev_handler_info_show,
2631 };
2632
2633 struct proc_dir_entry *scst_create_proc_entry(struct proc_dir_entry *root,
2634         const char *name, struct scst_proc_data *pdata)
2635 {
2636         struct proc_dir_entry *p = NULL;
2637
2638         TRACE_ENTRY();
2639
2640         if (root) {
2641                 mode_t mode;
2642
2643                 mode = S_IFREG | S_IRUGO | (pdata->seq_op.write ? S_IWUSR : 0);
2644                 p = create_proc_entry(name, mode, root);
2645                 if (p == NULL) {
2646                         PRINT_ERROR("Fail to create entry %s in /proc", name);
2647                 } else {
2648                         p->proc_fops = &pdata->seq_op;
2649                         p->data = pdata->data;
2650                 }
2651         }
2652
2653         TRACE_EXIT();
2654         return p;
2655 }
2656 EXPORT_SYMBOL(scst_create_proc_entry);
2657
2658 int scst_single_seq_open(struct inode *inode, struct file *file)
2659 {
2660 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
2661         struct scst_proc_data *pdata = container_of(PDE(inode)->proc_fops,
2662                 struct scst_proc_data, seq_op);
2663 #else
2664         struct scst_proc_data *pdata = container_of(inode->i_fop,
2665                 struct scst_proc_data, seq_op);
2666 #endif
2667         return single_open(file, pdata->show, PDE(inode)->data);
2668 }
2669 EXPORT_SYMBOL(scst_single_seq_open);
2670
2671 struct proc_dir_entry *scst_proc_get_tgt_root(
2672         struct scst_tgt_template *vtt)
2673 {
2674         return vtt->proc_tgt_root;
2675 }
2676 EXPORT_SYMBOL(scst_proc_get_tgt_root);
2677
2678 struct proc_dir_entry *scst_proc_get_dev_type_root(
2679         struct scst_dev_type *dtt)
2680 {
2681         return dtt->proc_dev_type_root;
2682 }
2683 EXPORT_SYMBOL(scst_proc_get_dev_type_root);