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