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