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