/proc implementation moved to seq_*() library. Mostly done by Ming Zhang.
authorvlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Thu, 14 Dec 2006 17:08:46 +0000 (17:08 +0000)
committervlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Thu, 14 Dec 2006 17:08:46 +0000 (17:08 +0000)
git-svn-id: https://scst.svn.sourceforge.net/svnroot/scst/trunk@57 d57e44dd-8a1f-0410-8b47-8ef2f437770f

mpt/mpt_scst.c
qla2x00t/qla2x00-target/qla2x00t.c
scst/include/scsi_tgt.h
scst/src/dev_handlers/scst_dev_handler.h
scst/src/dev_handlers/scst_fileio.c
scst/src/scst.c
scst/src/scst_proc.c

index cab09ae..9b24aa9 100644 (file)
@@ -287,7 +287,7 @@ static struct scst_tgt_template tgt_template = {
        .rdy_to_xfer = mpt_rdy_to_xfer,
        .on_free_cmd = mpt_on_free_cmd,
        .task_mgmt_fn_done = mpt_task_mgmt_fn_done,
-       .proc_info = mpt_proc_info,
+//ToDo:        .proc_info = mpt_proc_info,
 };
 
 static inline void 
index 8579c36..ade7b0e 100644 (file)
@@ -28,7 +28,7 @@
 #include <scsi/scsi_host.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
-
+#include <linux/seq_file.h>
 #include <linux/list.h>
 
 #include <scsi_tgt.h>
@@ -2054,36 +2054,40 @@ out:
 
 #include <linux/proc_fs.h>
 
-static int qla2x00t_proc_log_entry_read(char *buffer, char **start,
-                                       off_t offset, int length, int *eof,
-                                       void *data)
+static int q2t_log_info_show(struct seq_file *seq, void *v)
 {
        int res = 0;
 
        TRACE_ENTRY();
-       res = scst_proc_log_entry_read(buffer, start, offset, length, eof,
-                                      data, trace_flag, NULL);
+
+       res = scst_proc_log_entry_read(seq, trace_flag, NULL);
 
        TRACE_EXIT_RES(res);
        return res;
 }
 
-static int qla2x00t_proc_log_entry_write(struct file *file, const char *buf,
-                                        unsigned long length, void *data)
+static int q2t_proc_log_entry_write(struct file *file, const char __user *buf,
+                                        size_t length, loff_t *off)
 {
        int res = 0;
 
        TRACE_ENTRY();
 
-       res = scst_proc_log_entry_write(file, buf, length, data,
-                       &trace_flag, SCST_DEFAULT_QLA_LOG_FLAGS, NULL);
+       res = scst_proc_log_entry_write(file, buf, length, &trace_flag,
+               SCST_DEFAULT_QLA_LOG_FLAGS, NULL);
 
        TRACE_EXIT_RES(res);
        return res;
 }
+
+
+static struct scst_proc_data q2t_log_proc_data = {
+       SCST_DEF_RW_SEQ_OP(q2t_proc_log_entry_write)
+       .show = q2t_log_info_show,
+};
 #endif
 
-static int qla2x00t_proc_log_entry_build(struct scst_tgt_template *templ)
+static int q2t_proc_log_entry_build(struct scst_tgt_template *templ)
 {
        int res = 0;
 #if defined(DEBUG) || defined(TRACING)
@@ -2094,10 +2098,9 @@ static int qla2x00t_proc_log_entry_build(struct scst_tgt_template *templ)
 
        if (root) {
                /* create the proc file entry for the device */
-               p = create_proc_read_entry(Q2T_PROC_LOG_ENTRY_NAME,
-                                          S_IFREG | S_IRUGO | S_IWUSR, root,
-                                          qla2x00t_proc_log_entry_read,
-                                          (void *)templ->name);
+               q2t_log_proc_data.data = (void *)templ->name;
+               p = scst_create_proc_entry(root, Q2T_PROC_LOG_ENTRY_NAME,
+                                       &q2t_log_proc_data);
                if (p == NULL) {
                        PRINT_ERROR("Not enough memory to register "
                             "target driver %s entry %s in /proc",
@@ -2105,7 +2108,6 @@ static int qla2x00t_proc_log_entry_build(struct scst_tgt_template *templ)
                        res = -ENOMEM;
                        goto out;
                }
-               p->write_proc = qla2x00t_proc_log_entry_write;
        }
 
 out:
@@ -2115,7 +2117,7 @@ out:
        return res;
 }
 
-static void qla2x00t_proc_log_entry_clean(struct scst_tgt_template *templ)
+static void q2t_proc_log_entry_clean(struct scst_tgt_template *templ)
 {
 #if defined(DEBUG) || defined(TRACING)
        struct proc_dir_entry *root;
@@ -2131,7 +2133,7 @@ static void qla2x00t_proc_log_entry_clean(struct scst_tgt_template *templ)
 #endif
 }
 
-static int __init qla2x00t_init(void)
+static int __init q2t_init(void)
 {
        int res = 0;
 
@@ -2154,7 +2156,7 @@ static int __init qla2x00t_init(void)
         * called via scst_register_target_template()
         */
 
-       res = qla2x00t_proc_log_entry_build(&tgt_template);
+       res = q2t_proc_log_entry_build(&tgt_template);
        if (res < 0) {
                goto out_unreg_target;
        }
@@ -2169,11 +2171,11 @@ out_unreg_target:
        goto out;
 }
 
-static void __exit qla2x00t_exit(void)
+static void __exit q2t_exit(void)
 {
        TRACE_ENTRY();
 
-       qla2x00t_proc_log_entry_clean(&tgt_template);
+       q2t_proc_log_entry_clean(&tgt_template);
 
        scst_unregister_target_template(&tgt_template);
        
@@ -2185,8 +2187,8 @@ static void __exit qla2x00t_exit(void)
        return;
 }
 
-module_init(qla2x00t_init);
-module_exit(qla2x00t_exit);
+module_init(q2t_init);
+module_exit(q2t_exit);
 
 MODULE_AUTHOR("Vladislav Bolkhovitin & Leonid Stoljar & Nathaniel Clark");
 MODULE_DESCRIPTION("Target mode logic for qla2xxx");
index df15e41..3b0dac5 100644 (file)
 #include <linux/version.h>
 #include <linux/blkdev.h>
 #include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+
 #ifdef SCST_HIGHMEM
 #include <asm/kmap_types.h>
 #endif
-#include <../drivers/scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi.h>
 
 /* Version numbers, the same as for the kernel */
 #define SCST_VERSION_CODE 0x000906
@@ -694,14 +699,15 @@ struct scst_tgt_template
         */
        int (*report_aen) (int mgmt_fn, const uint8_t *lun, int lun_len);
 
-       /* 
-        * This function can be used to export the driver's statistics and 
-        * other infos to the world outside the kernel. 
+       /*
+        * Those functions can be used to export the driver's statistics and
+        * other infos to the world outside the kernel.
         *
         * OPTIONAL
         */
-       int (*proc_info) (char *buffer, char **start, off_t offset, 
-               int length, int *eof, struct scst_tgt *tgt, int inout);
+       int (*read_proc) (struct seq_file *seq, struct scst_tgt *tgt);
+       int (*write_proc) (char *buffer, char **start, off_t offset, 
+               int length, int *eof, struct scst_tgt *tgt);
 
        /* 
         * Name of the template. Must be unique to identify
@@ -836,15 +842,15 @@ struct scst_dev_type
        int (*task_mgmt_fn) (struct scst_mgmt_cmd *mgmt_cmd, 
                struct scst_tgt_dev *tgt_dev);
 
-       /* 
-        * This function can be used to export the handler's statistics and 
-        * other infos to the world outside the kernel. 
+       /*
+        * Those functions can be used to export the handler's statistics and
+        * other infos to the world outside the kernel.
         *
         * OPTIONAL
         */
-       int (*proc_info) (char *buffer, char **start, off_t offset,
-               int length, int *eof, struct scst_dev_type *dev_type, 
-               int inout);
+       int (*read_proc) (struct seq_file *seq, struct scst_dev_type *dev_type);
+       int (*write_proc) (char *buffer, char **start, off_t offset,
+               int length, int *eof, struct scst_dev_type *dev_type);
 
        struct module *module;
 
@@ -2149,14 +2155,37 @@ struct scst_proc_log {
        const char *token;
 };
 
-int scst_proc_log_entry_read(char *buffer, char **start,
-       off_t offset, int length, int *eof, void *data,
-       unsigned long log_level, const struct scst_proc_log *tbl);
+int scst_proc_log_entry_read(struct seq_file *seq, unsigned long log_level, 
+       const struct scst_proc_log *tbl);
 
 int scst_proc_log_entry_write(struct file *file, const char *buf,
-       unsigned long length, void *data, unsigned long *log_level,
+       unsigned long length, unsigned long *log_level,
        unsigned long default_level, const struct scst_proc_log *tbl);
 
+/*
+ * helper data structure and function to create proc entry.
+ */
+struct scst_proc_data {
+       struct file_operations seq_op;
+       int (*show)(struct seq_file *, void *);
+       void *data;
+};
+
+int scst_single_seq_open(struct inode *inode, struct file *file);
+
+struct proc_dir_entry *scst_create_proc_entry(struct proc_dir_entry * root,
+        const char *name, struct scst_proc_data *pdata);
+
+#define SCST_DEF_RW_SEQ_OP(x)                          \
+       .seq_op = {                                    \
+               .owner          = THIS_MODULE,         \
+               .open           = scst_single_seq_open,\
+               .read           = seq_read,            \
+               .write          = x,                   \
+               .llseek         = seq_lseek,           \
+               .release        = single_release,      \
+       },
+
 /*
  * Adds and deletes (stops) num SCST's threads. Returns 0 on success,
  * error code otherwise.
index e6fdc59..31f08e1 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <linux/module.h>
 #include <linux/proc_fs.h>
+#include <linux/seq_file.h>
 
 #ifdef DEBUG
 #define SCST_DEFAULT_DEV_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_PID | \
 #define SCST_DEFAULT_DEV_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MGMT | TRACE_MINOR)
 #endif
 
-static int scst_dev_handler_proc_log_entry_read(char *buffer, char **start,
-       off_t offset, int length, int *eof, void *data)
+static struct scst_proc_data dev_handler_log_proc_data;
+
+static int dev_handler_log_info_show(struct seq_file *seq, void *v)
 {
        int res = 0;
 
        TRACE_ENTRY();
-       res = scst_proc_log_entry_read(buffer, start, offset, length, eof,
-                                      data, trace_flag, NULL);
+       res = scst_proc_log_entry_read(seq, trace_flag, NULL);
 
        TRACE_EXIT_RES(res);
        return res;
 }
 
-static int scst_dev_handler_proc_log_entry_write(struct file *file,
-       const char *buf, unsigned long length, void *data)
+static int scst_dev_handler_proc_log_entry_write(struct file *file, const char __user *buf,
+       size_t length, loff_t *off)
 {
        int res = 0;
 
        TRACE_ENTRY();
 
-       res = scst_proc_log_entry_write(file, buf, length, data,
-                               &trace_flag, SCST_DEFAULT_DEV_LOG_FLAGS, NULL);
+       res = scst_proc_log_entry_write(file, buf, length, &trace_flag,
+                       SCST_DEFAULT_DEV_LOG_FLAGS, NULL);
 
        TRACE_EXIT_RES(res);
        return res;
@@ -57,10 +58,9 @@ static int scst_dev_handler_build_std_proc(struct scst_dev_type *dev_type)
 
        if (root) {
                /* create the proc file entry for the device */
-               p = create_proc_read_entry(DEV_HANDLER_LOG_ENTRY_NAME,
-                                          S_IFREG | S_IRUGO | S_IWUSR, root,
-                                          scst_dev_handler_proc_log_entry_read,
-                                          (void *)dev_type->name);
+               dev_handler_log_proc_data.data = (void *)dev_type->name;
+               p = scst_create_proc_entry(root, DEV_HANDLER_LOG_ENTRY_NAME,
+                                          &dev_handler_log_proc_data);
                if (p == NULL) {
                        PRINT_ERROR_PR("Not enough memory to register dev "
                             "handler %s entry %s in /proc",
@@ -68,7 +68,6 @@ static int scst_dev_handler_build_std_proc(struct scst_dev_type *dev_type)
                        res = -ENOMEM;
                        goto out;
                }
-               p->write_proc = scst_dev_handler_proc_log_entry_write;
        }
 
 out:
@@ -93,3 +92,9 @@ static void scst_dev_handler_destroy_std_proc(struct scst_dev_type *dev_type)
 #endif
 }
 
+#if defined(DEBUG) || defined(TRACING)
+static struct scst_proc_data dev_handler_log_proc_data = {
+       SCST_DEF_RW_SEQ_OP(scst_dev_handler_proc_log_entry_write)
+       .show = dev_handler_log_info_show,
+};
+#endif
index 9010ca9..02f6b3e 100644 (file)
@@ -141,12 +141,12 @@ static void fileio_exec_read_toc(struct scst_cmd *cmd);
 static void fileio_exec_prevent_allow_medium_removal(struct scst_cmd *cmd);
 static int fileio_fsync(struct scst_fileio_tgt_dev *ftgt_dev,
        loff_t loff, loff_t len, struct scst_cmd *cmd);
-static int disk_fileio_proc(char *buffer, char **start, off_t offset,
-       int length, int *eof, struct scst_dev_type *dev_type, int inout);
-static int cdrom_fileio_proc(char *buffer, char **start, off_t offset,
-       int length, int *eof, struct scst_dev_type *dev_type, int inout);
-static int fileio_proc_help_read(char *buffer, char **start,off_t offset,
-       int length, int *eof, void *data);
+static int disk_fileio_read_proc(struct seq_file *seq, struct scst_dev_type *dev_type);
+static int disk_fileio_write_proc(char *buffer, char **start, off_t offset,
+       int length, int *eof, struct scst_dev_type *dev_type);
+static int cdrom_fileio_read_proc(struct seq_file *seq, struct scst_dev_type *dev_type);
+static int cdrom_fileio_write_proc(char *buffer, char **start, off_t offset,
+       int length, int *eof, struct scst_dev_type *dev_type);
 
 #define DISK_TYPE_FILEIO {             \
   name:         DISK_FILEIO_NAME,      \
@@ -161,7 +161,8 @@ static int fileio_proc_help_read(char *buffer, char **start,off_t offset,
   parse:        disk_fileio_parse,     \
   exec:         disk_fileio_exec,      \
   task_mgmt_fn: fileio_task_mgmt_fn,   \
-  proc_info:    disk_fileio_proc,      \
+  read_proc:    disk_fileio_read_proc, \
+  write_proc:   disk_fileio_write_proc,        \
 }
 
 #define CDROM_TYPE_FILEIO {            \
@@ -177,7 +178,8 @@ static int fileio_proc_help_read(char *buffer, char **start,off_t offset,
   parse:        cdrom_fileio_parse,    \
   exec:         cdrom_fileio_exec,     \
   task_mgmt_fn: fileio_task_mgmt_fn,   \
-  proc_info:    cdrom_fileio_proc,     \
+  read_proc:    cdrom_fileio_read_proc,        \
+  write_proc:   cdrom_fileio_write_proc,\
 }
 
 DECLARE_MUTEX(scst_fileio_mutex);
@@ -2262,349 +2264,285 @@ out:
        return dev;
 }
 
-struct fileio_proc_update_struct {
-       int len, plen, pplen;
-       off_t begin, pbegin, ppbegin;
-       off_t pos;
-};
-
-static int fileio_proc_update_size(int size, off_t offset, int length,
-       struct fileio_proc_update_struct *p, int is_start)
+/* 
+ * Called when a file in the /proc/DISK_FILEIO_NAME/DISK_FILEIO_NAME is read
+ */
+static int disk_fileio_read_proc(struct seq_file *seq, struct scst_dev_type *dev_type)
 {
        int res = 0;
-       if (size > 0) {
-               p->len += size;
-               p->pos = p->begin + p->len;
-               if (p->pos <= offset) {
-                       p->len = 0;
-                       p->begin = p->pos;
-               } else if (p->pos >= offset + length) {
-                       res = 1;
-                       goto out;
-               } else
-                       res = 0;
-       } else {
-               p->begin = p->ppbegin;
-               p->len = p->pplen;
-               res = 1;
+       struct scst_fileio_dev *virt_dev;
+
+       TRACE_ENTRY();
+       
+       if (down_interruptible(&scst_fileio_mutex) != 0) {
+               res = -EINTR;
                goto out;
        }
-       if (is_start) {
-               p->ppbegin = p->pbegin;
-               p->pplen = p->plen;
-               p->pbegin = p->begin;
-               p->plen = p->len;
+       
+       seq_printf(seq, "%-17s %-11s %-11s %-15s %s\n",
+                          "Name", "Size(MB)", "Block size", "Options", "File name");
+
+       list_for_each_entry(virt_dev, &disk_fileio_dev_list, fileio_dev_list_entry) {
+               int c;
+               seq_printf(seq, "%-17s %-11d %-12d", virt_dev->name,
+                       (uint32_t)(virt_dev->file_size >> 20),
+                       virt_dev->block_size);
+               c = 0;
+               if (virt_dev->wt_flag) {
+                       seq_printf(seq, "WT ");
+                       c += 3;
+               }
+               if (virt_dev->nv_cache) {
+                       seq_printf(seq, "NV ");
+                       c += 3;
+               }
+               if (virt_dev->rd_only_flag) {
+                       seq_printf(seq, "RO ");
+                       c += 3;
+               }
+               if (virt_dev->o_direct_flag) {
+                       seq_printf(seq, "DR ");
+                       c += 3;
+               }
+               if (virt_dev->nullio) {
+                       seq_printf(seq, "NIO ");
+                       c += 4;
+               }
+               while (c < 16) {
+                       seq_printf(seq, " ");
+                       c++;
+               }
+               seq_printf(seq, "%s\n", virt_dev->file_name);
        }
+       up(&scst_fileio_mutex);
 out:
+       TRACE_EXIT_RES(res);
        return res;
 }
 
 /* 
- * Called when a file in the /proc/DISK_FILEIO_NAME/DISK_FILEIO_NAME is read
- * or written 
+ * Called when a file in the /proc/DISK_FILEIO_NAME/DISK_FILEIO_NAME is written
  */
-static int disk_fileio_proc(char *buffer, char **start, off_t offset,
-       int length, int *eof, struct scst_dev_type *dev_type, int inout)
+static int disk_fileio_write_proc(char *buffer, char **start, off_t offset,
+       int length, int *eof, struct scst_dev_type *dev_type)
 {
        int res = 0, action;
        char *p, *name, *file_name;
        struct scst_fileio_dev *virt_dev, *vv;
-       int size;
-       struct fileio_proc_update_struct pu;
+       uint32_t block_size = DEF_DISK_BLOCKSIZE;
+       int block_shift = DEF_DISK_BLOCKSIZE_SHIFT;
+       size_t len;
 
        TRACE_ENTRY();
-
-       memset(&pu, 0, sizeof(pu));
        
        /* VERY UGLY code. You can rewrite it if you want */
+
+       if (buffer[0] == '\0')
+               goto out;
        
        if (down_interruptible(&scst_fileio_mutex) != 0) {
                res = -EINTR;
                goto out;
        }
-       
-       if (inout == 0) { /* read */
-               size = scnprintf(buffer, length, "%-17s %-11s %-11s %-15s %s\n",
-                              "Name", "Size(MB)", "Block size", "Options", "File name");
-               if (fileio_proc_update_size(size, offset, length, &pu, 1))
-                       goto stop_output;
-
-               list_for_each_entry(virt_dev, &disk_fileio_dev_list, 
-                       fileio_dev_list_entry)
+
+       p = buffer;
+       if (p[strlen(p) - 1] == '\n') {
+               p[strlen(p) - 1] = '\0';
+       }
+       if (!strncmp("close ", p, 6)) {
+               p += 6;
+               action = 0;
+       } else if (!strncmp("open ", p, 5)) {
+               p += 5;
+               action = 2;
+       } else {
+               PRINT_ERROR_PR("Unknown action \"%s\"", p);
+               res = -EINVAL;
+               goto out_up;
+       }
+
+       while (isspace(*p) && *p != '\0')
+               p++;
+       name = p;
+       while (!isspace(*p) && *p != '\0')
+               p++;
+       *p++ = '\0';
+       if (*name == '\0') {
+               PRINT_ERROR_PR("%s", "Name required");
+               res = -EINVAL;
+               goto out_up;
+       } else if (strlen(name) >= sizeof(virt_dev->name)) {
+               PRINT_ERROR_PR("Name is too long (max %zd "
+                       "characters)", sizeof(virt_dev->name)-1);
+               res = -EINVAL;
+               goto out_up;
+       }
+
+       if (action) {                      /* open */
+               virt_dev = NULL;
+               list_for_each_entry(vv, &disk_fileio_dev_list,
+                                       fileio_dev_list_entry)
                {
-                       int c;
-                       size = scnprintf(buffer + pu.len, length - pu.len, 
-                               "%-17s %-11d %-12d", virt_dev->name,
-                               (uint32_t)(virt_dev->file_size >> 20),
-                               virt_dev->block_size);
-                       if (fileio_proc_update_size(size, offset, length, &pu,
-                                       1)) {
-                               goto stop_output;
-                       }
-                       c = 0;
-                       if (virt_dev->wt_flag) {
-                               size = scnprintf(buffer + pu.len, length - pu.len, "WT");
-                               c += size;
-                               if (fileio_proc_update_size(size, offset,
-                                               length, &pu, 0)) {
-                                       goto stop_output;
-                               }
-                       }
-                       if (virt_dev->nv_cache) {
-                               size = scnprintf(buffer + pu.len, length - pu.len,
-                                       c ? ",NV" : "NV");
-                               c += size;
-                               if (fileio_proc_update_size(size, offset,
-                                               length, &pu, 0)) {
-                                       goto stop_output;
-                               }
-                       }
-                       if (virt_dev->rd_only_flag) {
-                               size = scnprintf(buffer + pu.len, length - pu.len, 
-                                       c ? ",RO" : "RO");
-                               c += size;
-                               if (fileio_proc_update_size(size, offset,
-                                               length, &pu, 0)) {
-                                       goto stop_output;
-                               }
-                       }
-                       if (virt_dev->o_direct_flag) {
-                               size = scnprintf(buffer + pu.len, length - pu.len, 
-                                       c ? ",DR" : "DR");
-                               c += size;
-                               if (fileio_proc_update_size(size, offset,
-                                               length, &pu, 0)) {
-                                       goto stop_output;
-                               }
-                       }
-                       if (virt_dev->nullio) {
-                               size = scnprintf(buffer + pu.len, length - pu.len, 
-                                       c ? ",NIO" : "NIO");
-                               c += size;
-                               if (fileio_proc_update_size(size, offset,
-                                               length, &pu, 0)) {
-                                       goto stop_output;
-                               }
-                       }
-                       while (c < 16) {
-                               size = scnprintf(buffer + pu.len, length - pu.len, " ");
-                               if (fileio_proc_update_size(size, offset,
-                                               length, &pu, 0)) {
-                                       goto stop_output;
-                               }
-                               c++;
-                       }
-                       size = scnprintf(buffer + pu.len, length - pu.len, "%s\n",
-                                       virt_dev->file_name);
-                       if (fileio_proc_update_size(size, offset, length, &pu,
-                                       0)) {
-                               goto stop_output;
+                       if (strcmp(vv->name, name) == 0) {
+                               virt_dev = vv;
+                               break;
                        }
                }
-               *eof = 1;
-               goto stop_output;
-       } else {  /* write */
-               uint32_t block_size = DEF_DISK_BLOCKSIZE;
-               int block_shift = DEF_DISK_BLOCKSIZE_SHIFT;
-               p = buffer;
-               if (p[strlen(p) - 1] == '\n') {
-                       p[strlen(p) - 1] = '\0';
-               }
-               if (!strncmp("close ", p, 6)) {
-                       p += 6;
-                       action = 0;
-               } else if (!strncmp("open ", p, 5)) {
-                       p += 5;
-                       action = 2;
-               } else {
-                       PRINT_ERROR_PR("Unknown action \"%s\"", p);
+               if (virt_dev) {
+                       PRINT_ERROR_PR("Virtual device with name "
+                                  "%s already exist", name);
                        res = -EINVAL;
                        goto out_up;
                }
 
                while (isspace(*p) && *p != '\0')
                        p++;
-               name = p;
+               file_name = p;
                while (!isspace(*p) && *p != '\0')
                        p++;
                *p++ = '\0';
-               if (*name == '\0') {
-                       PRINT_ERROR_PR("%s", "Name required");
+               if (*file_name == '\0') {
+                       PRINT_ERROR_PR("%s", "File name required");
                        res = -EINVAL;
                        goto out_up;
-               } else if (strlen(name) >= sizeof(virt_dev->name)) {
-                       PRINT_ERROR_PR("Name is too long (max %zd "
-                               "characters)", sizeof(virt_dev->name)-1);
+               } else if (*file_name != '/') {
+                       PRINT_ERROR_PR("File path \"%s\" is not "
+                               "absolute", file_name);
                        res = -EINVAL;
                        goto out_up;
                }
 
-               if (action) {                      /* open */
-                       virt_dev = NULL;
-                       list_for_each_entry(vv, &disk_fileio_dev_list,
-                                           fileio_dev_list_entry)
-                       {
-                               if (strcmp(vv->name, name) == 0) {
-                                       virt_dev = vv;
-                                       break;
-                               }
-                       }
-                       if (virt_dev) {
-                               PRINT_ERROR_PR("Virtual device with name "
-                                      "%s already exist", name);
-                               res = -EINVAL;
-                               goto out_up;
-                       }
+               virt_dev = fileio_alloc_dev();
+               if (virt_dev == NULL) {
+                       TRACE(TRACE_OUT_OF_MEM, "%s",
+                                 "Allocation of virt_dev failed");
+                       res = -ENOMEM;
+                       goto out_up;
+               }
 
-                       while (isspace(*p) && *p != '\0')
-                               p++;
-                       file_name = p;
-                       while (!isspace(*p) && *p != '\0')
-                               p++;
-                       *p++ = '\0';
-                       if (*file_name == '\0') {
-                               PRINT_ERROR_PR("%s", "File name required");
-                               res = -EINVAL;
-                               goto out_up;
-                       } else if (*file_name != '/') {
-                               PRINT_ERROR_PR("File path \"%s\" is not "
-                                       "absolute", file_name);
-                               res = -EINVAL;
-                               goto out_up;
-                       }
+               while (isspace(*p) && *p != '\0')
+                       p++;
 
-                       virt_dev = fileio_alloc_dev();
-                       if (virt_dev == NULL) {
-                               TRACE(TRACE_OUT_OF_MEM, "%s",
-                                     "Allocation of virt_dev failed");
-                               res = -ENOMEM;
-                               goto out_up;
+               if (isdigit(*p)) {
+                       char *pp;
+                       uint32_t t;
+                       block_size = simple_strtoul(p, &pp, 0);
+                       p = pp;
+                       if ((*p != '\0') && !isspace(*p)) {
+                               PRINT_ERROR_PR("Parse error: \"%s\"", p);
+                               res = -EINVAL;
+                               goto out_free_vdev;
                        }
-
                        while (isspace(*p) && *p != '\0')
                                p++;
 
-                       if (isdigit(*p)) {
-                               char *pp;
-                               uint32_t t;
-                               block_size = simple_strtoul(p, &pp, 0);
-                               p = pp;
-                               if ((*p != '\0') && !isspace(*p)) {
-                                       PRINT_ERROR_PR("Parse error: \"%s\"", p);
-                                       res = -EINVAL;
-                                       goto out_free_vdev;
-                               }
-                               while (isspace(*p) && *p != '\0')
-                                       p++;
-
-                               t = block_size;
-                               block_shift = 0;
-                               while(1) {
-                                       if ((t & 1) != 0)
-                                               break;
-                                       t >>= 1;
-                                       block_shift++;
-                               }
-                               if (block_shift < 9) {
-                                       PRINT_ERROR_PR("Wrong block size %d",
-                                               block_size);
-                                       res = -EINVAL;
-                                       goto out_free_vdev;
-                               }
+                       t = block_size;
+                       block_shift = 0;
+                       while(1) {
+                               if ((t & 1) != 0)
+                                       break;
+                               t >>= 1;
+                               block_shift++;
                        }
-                       virt_dev->block_size = block_size;
-                       virt_dev->block_shift = block_shift;
-                       
-                       while (*p != '\0') {
-                               if (!strncmp("WRITE_THROUGH", p, 13)) {
-                                       p += 13;
-                                       virt_dev->wt_flag = 1;
-                                       TRACE_DBG("%s", "WRITE_THROUGH");
-                               } else if (!strncmp("NV_CACHE", p, 8)) {
-                                       p += 8;
-                                       virt_dev->nv_cache = 1;
-                                       TRACE_DBG("%s", "NON-VOLATILE CACHE");
-                               } else if (!strncmp("READ_ONLY", p, 9)) {
-                                       p += 9;
-                                       virt_dev->rd_only_flag = 1;
-                                       TRACE_DBG("%s", "READ_ONLY");
-                               } else if (!strncmp("O_DIRECT", p, 8)) {
-                                       p += 8;
-                       #if 0
-                                       
-                                       virt_dev->o_direct_flag = 1;
-                                       TRACE_DBG("%s", "O_DIRECT");
-                       #else
-                                       PRINT_INFO_PR("%s flag doesn't currently"
-                                           " work, ignoring it", "O_DIRECT");
-                       #endif
-                               } else if (!strncmp("NULLIO", p, 6)) {
-                                       p += 6;
-                                       virt_dev->nullio = 1;
-                                       TRACE_DBG("%s", "NULLIO");
-                               } else {
-                                       PRINT_ERROR_PR("Unknown flag \"%s\"", p);
-                                       res = -EINVAL;
-                                       goto out_free_vdev;
-                               }
-                               while (isspace(*p) && *p != '\0')
-                                       p++;
+                       if (block_shift < 9) {
+                               PRINT_ERROR_PR("Wrong block size %d",
+                                       block_size);
+                               res = -EINVAL;
+                               goto out_free_vdev;
                        }
-                       
-                       strcpy(virt_dev->name, name);
-
-                       pu.len = strlen(file_name) + 1;
-                       virt_dev->file_name = kmalloc(pu.len, GFP_KERNEL);
-                       if (virt_dev->file_name == NULL) {
-                               TRACE(TRACE_OUT_OF_MEM, "%s",
-                                     "Allocation of file_name failed");
-                               res = -ENOMEM;
+               }
+               virt_dev->block_size = block_size;
+               virt_dev->block_shift = block_shift;
+               
+               while (*p != '\0') {
+                       if (!strncmp("WRITE_THROUGH", p, 13)) {
+                               p += 13;
+                               virt_dev->wt_flag = 1;
+                               TRACE_DBG("%s", "WRITE_THROUGH");
+                       } else if (!strncmp("NV_CACHE", p, 8)) {
+                               p += 8;
+                               virt_dev->nv_cache = 1;
+                               TRACE_DBG("%s", "NON-VOLATILE CACHE");
+                       } else if (!strncmp("READ_ONLY", p, 9)) {
+                               p += 9;
+                               virt_dev->rd_only_flag = 1;
+                               TRACE_DBG("%s", "READ_ONLY");
+                       } else if (!strncmp("O_DIRECT", p, 8)) {
+                               p += 8;
+               #if 0
+                               
+                               virt_dev->o_direct_flag = 1;
+                               TRACE_DBG("%s", "O_DIRECT");
+               #else
+                               PRINT_INFO_PR("%s flag doesn't currently"
+                                       " work, ignoring it", "O_DIRECT");
+               #endif
+                       } else if (!strncmp("NULLIO", p, 6)) {
+                               p += 6;
+                               virt_dev->nullio = 1;
+                               TRACE_DBG("%s", "NULLIO");
+                       } else {
+                               PRINT_ERROR_PR("Unknown flag \"%s\"", p);
+                               res = -EINVAL;
                                goto out_free_vdev;
                        }
-                       strncpy(virt_dev->file_name, file_name, pu.len);
+                       while (isspace(*p) && *p != '\0')
+                               p++;
+               }
+               
+               strcpy(virt_dev->name, name);
+
+               len = strlen(file_name) + 1;
+               virt_dev->file_name = kmalloc(len, GFP_KERNEL);
+               if (virt_dev->file_name == NULL) {
+                       TRACE(TRACE_OUT_OF_MEM, "%s",
+                                 "Allocation of file_name failed");
+                       res = -ENOMEM;
+                       goto out_free_vdev;
+               }
+               strncpy(virt_dev->file_name, file_name, len);
 
-                       list_add_tail(&virt_dev->fileio_dev_list_entry,
-                                     &disk_fileio_dev_list);
+               list_add_tail(&virt_dev->fileio_dev_list_entry,
+                                 &disk_fileio_dev_list);
 
-                       virt_dev->virt_id =
-                           scst_register_virtual_device(&disk_devtype_fileio,
-                                                        virt_dev->name);
-                       if (virt_dev->virt_id < 0) {
-                               res = virt_dev->virt_id;
-                               goto out_free_vpath;
-                       }
-                       TRACE_DBG("Added virt_dev (name %s, file name %s, "
-                               "id %d, block size %d) to "
-                               "disk_fileio_dev_list", virt_dev->name,
-                               virt_dev->file_name, virt_dev->virt_id,
-                               virt_dev->block_size);
-               } else {                           /* close */
-                       virt_dev = NULL;
-                       list_for_each_entry(vv, &disk_fileio_dev_list,
-                                           fileio_dev_list_entry)
-                       {
-                               if (strcmp(vv->name, name) == 0) {
-                                       virt_dev = vv;
-                                       break;
-                               }
-                       }
-                       if (virt_dev == NULL) {
-                               PRINT_ERROR_PR("Device %s not found", name);
-                               res = -EINVAL;
-                               goto out_up;
+               virt_dev->virt_id =
+                       scst_register_virtual_device(&disk_devtype_fileio,
+                                                virt_dev->name);
+               if (virt_dev->virt_id < 0) {
+                       res = virt_dev->virt_id;
+                       goto out_free_vpath;
+               }
+               TRACE_DBG("Added virt_dev (name %s, file name %s, "
+                       "id %d, block size %d) to "
+                       "disk_fileio_dev_list", virt_dev->name,
+                       virt_dev->file_name, virt_dev->virt_id,
+                       virt_dev->block_size);
+       } else {                           /* close */
+               virt_dev = NULL;
+               list_for_each_entry(vv, &disk_fileio_dev_list,
+                                       fileio_dev_list_entry)
+               {
+                       if (strcmp(vv->name, name) == 0) {
+                               virt_dev = vv;
+                               break;
                        }
-                       scst_unregister_virtual_device(virt_dev->virt_id);
-                       PRINT_INFO_PR("Virtual device %s unregistered", 
-                               virt_dev->name);
-                       TRACE_DBG("virt_id %d unregister", virt_dev->virt_id);
+               }
+               if (virt_dev == NULL) {
+                       PRINT_ERROR_PR("Device %s not found", name);
+                       res = -EINVAL;
+                       goto out_up;
+               }
+               scst_unregister_virtual_device(virt_dev->virt_id);
+               PRINT_INFO_PR("Virtual device %s unregistered", 
+                       virt_dev->name);
+               TRACE_DBG("virt_id %d unregister", virt_dev->virt_id);
 
-                       list_del(&virt_dev->fileio_dev_list_entry);
+               list_del(&virt_dev->fileio_dev_list_entry);
 
-                       kfree(virt_dev->file_name);
-                       kfree(virt_dev);
-               }
-               res = length;
+               kfree(virt_dev->file_name);
+               kfree(virt_dev);
        }
+       res = length;
 
 out_up:
        up(&scst_fileio_mutex);
@@ -2613,14 +2551,6 @@ out:
        TRACE_EXIT_RES(res);
        return res;
 
-stop_output:
-       *start = buffer + (offset - pu.begin);
-       pu.len -= (offset - pu.begin);
-       if (pu.len > length)
-               pu.len = length;
-       res = max(0, pu.len);
-       goto out_up;
-
 out_free_vpath:
        list_del(&virt_dev->fileio_dev_list_entry);
        kfree(virt_dev->file_name);
@@ -2630,6 +2560,7 @@ out_free_vdev:
        goto out_up;
 }
 
+
 /* scst_fileio_mutex supposed to be held */
 static int cdrom_fileio_open(char *p, char *name)
 {
@@ -2640,8 +2571,7 @@ static int cdrom_fileio_open(char *p, char *name)
        int cdrom_empty;
 
        virt_dev = NULL;
-       list_for_each_entry(vv, &cdrom_fileio_dev_list,
-                           fileio_dev_list_entry)
+       list_for_each_entry(vv, &cdrom_fileio_dev_list, fileio_dev_list_entry)
        {
                if (strcmp(vv->name, name) == 0) {
                        virt_dev = vv;
@@ -2932,98 +2862,102 @@ out_err_resume:
 
 /* 
  * Called when a file in the /proc/CDROM_FILEIO_NAME/CDROM_FILEIO_NAME is read
- * or written 
  */
-static int cdrom_fileio_proc(char *buffer, char **start, off_t offset,
-       int length, int *eof, struct scst_dev_type *dev_type, int inout)
+static int cdrom_fileio_read_proc(struct seq_file *seq, struct scst_dev_type *dev_type)
+{
+       int res = 0;
+       struct scst_fileio_dev *virt_dev;
+
+       TRACE_ENTRY();
+
+       if (down_interruptible(&scst_fileio_mutex) != 0) {
+               res = -EINTR;
+               goto out;
+       }
+       
+       seq_printf(seq, "%-17s %-9s %s\n", "Name", "Size(MB)", "File name");
+
+       list_for_each_entry(virt_dev, &cdrom_fileio_dev_list, 
+               fileio_dev_list_entry) {
+               seq_printf(seq, "%-17s %-9d %s\n", virt_dev->name,
+                       (uint32_t)(virt_dev->file_size >> 20),
+                       virt_dev->file_name);
+       }
+
+       up(&scst_fileio_mutex);
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+/* 
+ * Called when a file in the /proc/CDROM_FILEIO_NAME/CDROM_FILEIO_NAME is written 
+ */
+static int cdrom_fileio_write_proc(char *buffer, char **start, off_t offset,
+       int length, int *eof, struct scst_dev_type *dev_type)
 {
        int res = 0, action;
        char *p, *name;
        struct scst_fileio_dev *virt_dev;
-       int size;
-       struct fileio_proc_update_struct pu;
 
        TRACE_ENTRY();
 
-       memset(&pu, 0, sizeof(pu));
-
        if (down_interruptible(&scst_fileio_mutex) != 0) {
                res = -EINTR;
                goto out;
        }
        
-       if (inout == 0) { /* read */
-               size = scnprintf(buffer, length, "%-17s %-9s %s\n",
-                              "Name", "Size(MB)", "File name");
-               if (fileio_proc_update_size(size, offset, length, &pu, 1))
-                       goto stop_output;
-
-               list_for_each_entry(virt_dev, &cdrom_fileio_dev_list, 
-                       fileio_dev_list_entry)
-               {
-                       size = scnprintf(buffer + pu.len, length - pu.len, 
-                               "%-17s %-9d %s\n", virt_dev->name,
-                               (uint32_t)(virt_dev->file_size >> 20),
-                               virt_dev->file_name);
-                       if (fileio_proc_update_size(size, offset, length, &pu,
-                                       1)) {
-                               goto stop_output;
-                       }
-               }
-               *eof = 1;
-               goto stop_output;
-       } else {  /* write */
-               p = buffer;
-               if (p[strlen(p) - 1] == '\n') {
-                       p[strlen(p) - 1] = '\0';
-               }
-               if (!strncmp("close ", p, 6)) {
-                       p += 6;
-                       action = 0;
-               } else if (!strncmp("change ", p, 5)) {
-                       p += 7;
-                       action = 1;
-               } else if (!strncmp("open ", p, 5)) {
-                       p += 5;
-                       action = 2;
-               } else {
-                       PRINT_ERROR_PR("Unknown action \"%s\"", p);
-                       res = -EINVAL;
-                       goto out_up;
-               }
+       p = buffer;
+       if (p[strlen(p) - 1] == '\n') {
+               p[strlen(p) - 1] = '\0';
+       }
+       if (!strncmp("close ", p, 6)) {
+               p += 6;
+               action = 0;
+       } else if (!strncmp("change ", p, 5)) {
+               p += 7;
+               action = 1;
+       } else if (!strncmp("open ", p, 5)) {
+               p += 5;
+               action = 2;
+       } else {
+               PRINT_ERROR_PR("Unknown action \"%s\"", p);
+               res = -EINVAL;
+               goto out_up;
+       }
 
-               while (isspace(*p) && *p != '\0')
-                       p++;
-               name = p;
-               while (!isspace(*p) && *p != '\0')
-                       p++;
-               *p++ = '\0';
-               if (*name == '\0') {
-                       PRINT_ERROR_PR("%s", "Name required");
-                       res = -EINVAL;
+       while (isspace(*p) && *p != '\0')
+               p++;
+       name = p;
+       while (!isspace(*p) && *p != '\0')
+               p++;
+       *p++ = '\0';
+       if (*name == '\0') {
+               PRINT_ERROR_PR("%s", "Name required");
+               res = -EINVAL;
+               goto out_up;
+       } else if (strlen(name) >= sizeof(virt_dev->name)) {
+               PRINT_ERROR_PR("Name is too long (max %zd "
+                       "characters)", sizeof(virt_dev->name)-1);
+               res = -EINVAL;
+               goto out_up;
+       }
+
+       if (action == 2) {                      /* open */
+               res = cdrom_fileio_open(p, name);
+               if (res != 0)
                        goto out_up;
-               } else if (strlen(name) >= sizeof(virt_dev->name)) {
-                       PRINT_ERROR_PR("Name is too long (max %zd "
-                               "characters)", sizeof(virt_dev->name)-1);
-                       res = -EINVAL;
+       } else if (action == 1) {          /* change */
+               res = cdrom_fileio_change(p, name);
+               if (res != 0)
+                       goto out_up;
+       } else {                           /* close */
+               res = cdrom_fileio_close(name);
+               if (res != 0)
                        goto out_up;
-               }
-
-               if (action == 2) {                      /* open */
-                       res = cdrom_fileio_open(p, name);
-                       if (res != 0)
-                               goto out_up;
-               } else if (action == 1) {          /* change */
-                       res = cdrom_fileio_change(p, name);
-                       if (res != 0)
-                               goto out_up;
-               } else {                           /* close */
-                       res = cdrom_fileio_close(name);
-                       if (res != 0)
-                               goto out_up;
-               }
-               res = length;
        }
+       res = length;
 
 out_up:
        up(&scst_fileio_mutex);
@@ -3031,16 +2965,25 @@ out_up:
 out:
        TRACE_EXIT_RES(res);
        return res;
+}
 
-stop_output:
-       *start = buffer + (offset - pu.begin);
-       pu.len -= (offset - pu.begin);
-       if (pu.len > length)
-               pu.len = length;
-       res = pu.len;
-       goto out_up;
+static int fileio_help_info_show(struct seq_file *seq, void *v)
+{
+       char *s = (char*)seq->private;
+
+       TRACE_ENTRY();
+
+       seq_printf(seq, "%s", s);
+
+       TRACE_EXIT();
+       return 0;
 }
 
+static struct scst_proc_data fileio_help_proc_data = {
+       SCST_DEF_RW_SEQ_OP(NULL)
+       .show = fileio_help_info_show,
+};
+
 static int fileio_proc_help_build(struct scst_dev_type *dev_type)
 {
        int res = 0;
@@ -3049,23 +2992,17 @@ static int fileio_proc_help_build(struct scst_dev_type *dev_type)
        TRACE_ENTRY();
 
        root = scst_proc_get_dev_type_root(dev_type);
-       if (root) {
-               p = create_proc_read_entry(FILEIO_PROC_HELP,
-                       S_IFREG | S_IRUGO, root,
-                       fileio_proc_help_read,
-                       (dev_type->type == TYPE_DISK) ? 
-                               disk_fileio_proc_help_string :
-                               cdrom_fileio_proc_help_string);
-               if (p == NULL) {
-                       PRINT_ERROR_PR("Not enough memory to register dev "
-                            "handler %s entry %s in /proc",
-                             dev_type->name, FILEIO_PROC_HELP);
-                       res = -ENOMEM;
-                       goto out;
-               }
+       fileio_help_proc_data.data = (dev_type->type == TYPE_DISK) ? 
+                                       disk_fileio_proc_help_string :
+                                       cdrom_fileio_proc_help_string;
+       p = scst_create_proc_entry(root, FILEIO_PROC_HELP, &fileio_help_proc_data);
+       if (p == NULL) {
+               PRINT_ERROR_PR("Not enough memory to register dev "
+                    "handler %s entry %s in /proc",
+                     dev_type->name, FILEIO_PROC_HELP);
+               res = -ENOMEM;
        }
 
-out:
        TRACE_EXIT_RES(res);
        return res;
 }
@@ -3083,21 +3020,6 @@ static void fileio_proc_help_destroy(struct scst_dev_type *dev_type)
        TRACE_EXIT();
 }
 
-static int fileio_proc_help_read(char *buffer, char **start, off_t offset,
-                                     int length, int *eof, void *data)
-{
-       int res = 0;
-       char *s = (char*)data;
-       
-       TRACE_ENTRY();
-       
-       if (offset < strlen(s))
-               res = scnprintf(buffer, length, "%s", &s[offset]);
-       
-       TRACE_EXIT_RES(res);
-       return res;
-}
-
 static int __init init_scst_fileio(struct scst_dev_type *devtype)
 {
        int res = 0;
index bf600df..6e724b9 100644 (file)
@@ -1254,6 +1254,9 @@ EXPORT_SYMBOL(scst_proc_log_entry_read);
 EXPORT_SYMBOL(scst_proc_log_entry_write);
 #endif
 
+EXPORT_SYMBOL(scst_create_proc_entry);
+EXPORT_SYMBOL(scst_single_seq_open);
+
 EXPORT_SYMBOL(__scst_get_buf);
 EXPORT_SYMBOL(scst_check_mem);
 EXPORT_SYMBOL(scst_get);
index d3758b3..3e0a6ca 100644 (file)
 #include <asm/string.h>
 #include <asm/uaccess.h>
 #include <linux/proc_fs.h>
+#include <linux/seq_file.h>
 
 #include "scst_debug.h"
 #include "scsi_tgt.h"
 #include "scst_mem.h"
 #include "scst_priv.h"
 
-static int scst_scsi_tgt_proc_info(char *buffer, char **start,
-                                  off_t offset, int length, int *eof,
-                                  void *data);
-static int scst_proc_scsi_tgt_gen_write(struct file *file,
-                                       const char *buf,
-                                       unsigned long length, void *data);
-static int scst_proc_version_read(char *buffer, char **start,off_t offset,
-                                 int length, int *eof, void *data);
-static int scst_proc_sessions_read(char *buffer, char **start, off_t offset,
-                                  int length, int *eof, void *data);
-static int scst_proc_help_read(char *buffer, char **start,off_t offset,
-                              int length, int *eof, void *data);
-static int scst_proc_threads_read(char *buffer, char **start,off_t offset,
-                                 int length, int *eof, void *data);
-static int scst_proc_threads_write(struct file *file, const char *buf,
-                                  unsigned long length, void *data);
-static int scst_proc_scsi_tgt_read(char *buffer, char **start, off_t offset,
-                                  int length, int *eof, void *data);
-static int scst_proc_scsi_tgt_write(struct file *file, const char *buf,
-                                   unsigned long count, void *data);
-static int scst_proc_scsi_dev_handler_read(char *buffer, char **start,
-                                          off_t offset, int length, int *eof,
-                                          void *data);
-static int scst_proc_scsi_dev_handler_write(struct file *file, const char *buf,
-                                           unsigned long count, void *data);
-static int scst_proc_scsi_dev_handler_type_read(char *buffer, char **start,
-                                               off_t offset, int length,
-                                               int *eof, void *data);
 static int scst_proc_init_groups(void);
 static void scst_proc_cleanup_groups(void);
 static int scst_proc_assign_handler(char *buf);
 static int scst_proc_group_add(const char *p);
-static int scst_proc_groups_devices_read(char *buffer, char **start,
-                                        off_t offset, int length, int *eof,
-                                        void *data);
-static int scst_proc_groups_devices_write(struct file *file, const char *buf,
-                                         unsigned long length, void *data);
-static int scst_proc_groups_names_read(char *buffer, char **start,
-                                      off_t offset, int length, int *eof,
-                                      void *data);
-static int scst_proc_groups_names_write(struct file *file, const char *buf,
-                                       unsigned long length, void *data);
 static int scst_proc_del_free_acg(struct scst_acg *acg, int remove_proc);
 
+static struct scst_proc_data scst_version_proc_data;
+static struct scst_proc_data scst_help_proc_data;
+static struct scst_proc_data scst_sgv_proc_data;
+static struct scst_proc_data scst_groups_names_proc_data;
+static struct scst_proc_data scst_groups_devices_proc_data;
+static struct scst_proc_data scst_sessions_proc_data;
+static struct scst_proc_data scst_dev_handler_type_proc_data;
+static struct scst_proc_data scst_log_proc_data;
+static struct scst_proc_data scst_tgt_proc_data;
+static struct scst_proc_data scst_threads_proc_data;
+static struct scst_proc_data scst_scsi_tgt_proc_data;
+static struct scst_proc_data scst_dev_handler_proc_data;
+
 /* 
  * Must be less than 4K page size, since our output routines 
  * use some slack for overruns 
@@ -217,90 +193,10 @@ static int strncasecmp(const char *s1, const char *s2, int n)
 
 #endif /* CONFIG_PPC */
 
-int scst_proc_read_tlb(const struct scst_proc_log *tbl, char *buffer,
-       int length, off_t offset, unsigned long log_level, int *first,
-       int *size, int *len, off_t *begin, off_t *pos)
-{
-       const struct scst_proc_log *t = tbl;
-       int res = 0;
-
-       while (t->token) {
-               if (log_level & t->val) {
-                       *size = scnprintf(buffer + *len, length - *len,
-                           "%s%s", *first ? "" : " | ", t->token);
-                       *first = 0;
-                       if (*size > 0) {
-                               *len += *size;
-                               *pos = *begin + *len;
-                               if (*pos <= offset) {
-                                       *len = 0;
-                                       *begin = *pos;
-                               } else if (*pos >= offset + length)
-                                       goto out_end;
-                       } else
-                               goto out_end;
-               }
-               t++;
-       }
-out:
-       return res;
-
-out_end:
-       res = 1;
-       goto out;
-}
-
 #if defined(DEBUG) || defined(TRACING)
 
-int scst_proc_log_entry_read(char *buffer, char **start,
-                            off_t offset, int length, int *eof, void *data,
-                            unsigned long log_level,
-                            const struct scst_proc_log *tbl)
-{
-       int res = 0, first = 1;
-       int size, len = 0;
-       off_t begin = 0, pos = 0;
-
-       TRACE_ENTRY();
-
-       TRACE_DBG("offset: %d, length %d", (int) offset, length);
-
-       if (scst_proc_read_tlb(scst_proc_trace_tbl, buffer, length, offset,
-                       log_level, &first, &size, &len, &begin, &pos))
-               goto stop_output;
-
-       if (tbl) {
-               TRACE_DBG("Reading private tlb, offset: %d, length %d",
-                       (int) offset, length);
-               if (scst_proc_read_tlb(tbl, buffer, length, offset, 
-                               log_level, &first, &size, &len, &begin, &pos))
-                       goto stop_output;
-       }
-
-       size = scnprintf(buffer + len, length - len, "%s\n", first ? "none" : "");
-       if (size > 0) {
-               len += size;
-               pos = begin + len;
-               if (pos <= offset) {
-                       len = 0;
-                       begin = pos;
-               } else if (pos >= offset + length)
-                       goto stop_output;
-       } else
-               goto stop_output;
-
-stop_output:
-       *start = buffer + (offset - begin);
-       len -= (offset - begin);
-       if (len > length)
-               len = length;
-       res = max(0, len);
-       TRACE_EXIT_RES(res);
-       return res;
-}
-
 int scst_proc_log_entry_write(struct file *file, const char *buf,
-       unsigned long length, void *data, unsigned long *log_level,
+       unsigned long length, unsigned long *log_level,
        unsigned long default_level, const struct scst_proc_log *tbl)
 {
        int res = length;
@@ -308,6 +204,7 @@ int scst_proc_log_entry_write(struct file *file, const char *buf,
        unsigned long level = 0, oldlevel;
        char *buffer, *p, *e;
        const struct scst_proc_log *t;
+       char *data = (char *)PDE(file->f_dentry->d_inode)->data;
 
        TRACE_ENTRY();
 
@@ -452,33 +349,8 @@ out:
        return res;
 }
 
-static int scst_scsi_tgt_proc_info_log(char *buffer, char **start,
-                                      off_t offset, int length, int *eof,
-                                      void *data)
-{
-       int res;
-
-       TRACE_ENTRY();
-
-
-       if (down_interruptible(&scst_proc_mutex) != 0) {
-               res = -EINTR;
-               goto out;
-       }
-
-       res = scst_proc_log_entry_read(buffer, start, offset, length, eof,
-                       data, trace_flag, scst_proc_local_trace_tbl);
-
-       up(&scst_proc_mutex);
-
-out:
-       TRACE_EXIT_RES(res);
-       return res;
-}
-
-static int scst_proc_scsi_tgt_gen_write_log(struct file *file,
-                                       const char *buf,
-                                       unsigned long length, void *data)
+static int scst_proc_scsi_tgt_gen_write_log(struct file *file, const char __user *buf,
+                                       size_t length, loff_t *off)
 {
        int res;
 
@@ -489,7 +361,7 @@ static int scst_proc_scsi_tgt_gen_write_log(struct file *file,
                goto out;
        }
 
-       res = scst_proc_log_entry_write(file, buf, length, data,
+       res = scst_proc_log_entry_write(file, buf, length,
                &trace_flag, SCST_DEFAULT_LOG_FLAGS, scst_proc_local_trace_tbl);
 
        up(&scst_proc_mutex);
@@ -501,7 +373,7 @@ out:
 
 #endif /* defined(DEBUG) || defined(TRACING) */
 
-static int scst_proc_init_module_log(void)
+static int __init scst_proc_init_module_log(void)
 {
        int res = 0;
 #if defined(DEBUG) || defined(TRACING)
@@ -509,14 +381,10 @@ static int scst_proc_init_module_log(void)
 
        TRACE_ENTRY();
 
-       generic = create_proc_read_entry(SCST_PROC_LOG_ENTRY_NAME,
-                                        S_IFREG | S_IRUGO | S_IWUSR,
-                                        scst_proc_scsi_tgt,
-                                        scst_scsi_tgt_proc_info_log,
-                                        (void *)"scsi_tgt");
-       if (generic) {
-               generic->write_proc = scst_proc_scsi_tgt_gen_write_log;
-       } else {
+       generic = scst_create_proc_entry(scst_proc_scsi_tgt,
+                                        SCST_PROC_LOG_ENTRY_NAME,
+                                        &scst_log_proc_data);
+       if (!generic) {
                PRINT_ERROR_PR("cannot init /proc/%s/%s",
                            SCST_PROC_ENTRY_NAME, SCST_PROC_LOG_ENTRY_NAME);
                res = -ENOMEM;
@@ -527,7 +395,7 @@ static int scst_proc_init_module_log(void)
        return res;
 }
 
-static void scst_proc_cleanup_module_log(void)
+static void __exit scst_proc_cleanup_module_log(void)
 {
 #if defined(DEBUG) || defined(TRACING)
        TRACE_ENTRY();
@@ -553,14 +421,11 @@ static int scst_proc_group_add_tree(struct scst_acg *acg, const char *p)
                goto out;
        }
 
-       generic = create_proc_read_entry(SCST_PROC_GROUPS_DEVICES_ENTRY_NAME,
-                                        S_IFREG | S_IRUGO | S_IWUSR,
-                                        acg->acg_proc_root,
-                                        scst_proc_groups_devices_read,
-                                        (void *)acg);
-       if (generic) {
-               generic->write_proc = scst_proc_groups_devices_write;
-       } else {
+       scst_groups_devices_proc_data.data = acg;
+       generic = scst_create_proc_entry(acg->acg_proc_root,
+                                        SCST_PROC_GROUPS_DEVICES_ENTRY_NAME,
+                                        &scst_groups_devices_proc_data);
+       if (!generic) {
                PRINT_ERROR_PR("cannot init /proc/%s/%s/%s/%s",
                               SCST_PROC_ENTRY_NAME,
                               SCST_PROC_GROUPS_ENTRY_NAME,
@@ -569,14 +434,11 @@ static int scst_proc_group_add_tree(struct scst_acg *acg, const char *p)
                goto out_remove;
        }
 
-       generic = create_proc_read_entry(SCST_PROC_GROUPS_USERS_ENTRY_NAME,
-                                        S_IFREG | S_IRUGO | S_IWUSR,
-                                        acg->acg_proc_root,
-                                        scst_proc_groups_names_read,
-                                        (void *)acg);
-       if (generic) {
-               generic->write_proc = scst_proc_groups_names_write;
-       } else {
+       scst_groups_names_proc_data.data = acg;
+       generic = scst_create_proc_entry(acg->acg_proc_root,
+                                        SCST_PROC_GROUPS_USERS_ENTRY_NAME,
+                                        &scst_groups_names_proc_data);
+       if (!generic) {
                PRINT_ERROR_PR("cannot init /proc/%s/%s/%s/%s",
                               SCST_PROC_ENTRY_NAME,
                               SCST_PROC_GROUPS_ENTRY_NAME,
@@ -674,7 +536,7 @@ static int scst_proc_del_free_acg(struct scst_acg *acg, int remove_proc)
        return res;
 }
 
-static int scst_proc_init_groups(void)
+static int __init scst_proc_init_groups(void)
 {
        int res = 0;
 
@@ -707,7 +569,7 @@ out_nomem:
        goto out;
 }
 
-static void scst_proc_cleanup_groups(void)
+static void __exit scst_proc_cleanup_groups(void)
 {
        struct scst_acg *acg_tmp, *acg;
 
@@ -728,133 +590,14 @@ static void scst_proc_cleanup_groups(void)
        TRACE_EXIT();
 }
 
-struct scst_proc_update_struct {
-       int len, plen, pplen;
-       off_t begin, pbegin, ppbegin;
-       off_t pos;
-};
-
-static int scst_proc_update_size(int size, off_t offset, int length,
-       struct scst_proc_update_struct *p)
-{
-       int res = 0;
-       if (size > 0) {
-               p->len += size;
-               p->pos = p->begin + p->len;
-               if (p->pos <= offset) {
-                       p->len = 0;
-                       p->begin = p->pos;
-               } else if (p->pos >= offset + length) {
-                       res = 1;
-                       goto out;
-               } else
-                       res = 0;
-       } else {
-               p->begin = p->ppbegin;
-               p->len = p->pplen;
-               res = 1;
-               goto out;
-       }
-       p->ppbegin = p->pbegin;
-       p->pplen = p->plen;
-       p->pbegin = p->begin;
-       p->plen = p->len;
-out:
-       return res;
-}
-
-static int scst_proc_sgv_read_1(char *buffer, off_t offset, int length,
-       struct scst_proc_update_struct *p,
-       const struct sgv_pool *pool, const char *name)
-{
-       int i, size;
-
-       size = scnprintf(buffer + p->len, length - p->len, "\n%-20s %-11d %-11d\n",
-               name, atomic_read(&pool->acc.hit_alloc),
-               atomic_read(&pool->acc.total_alloc));
-       if (scst_proc_update_size(size, offset, length, p))
-               return 1;
-
-       for(i = 0; i < SGV_POOL_ELEMENTS; i++) {
-               size = scnprintf(buffer + p->len, length - p->len, 
-                       "  %-18s %-11d %-11d\n", pool->cache_names[i], 
-                       atomic_read(&pool->cache_acc[i].hit_alloc),
-                       atomic_read(&pool->cache_acc[i].total_alloc));
-               if (scst_proc_update_size(size, offset, length, p))
-                       return 1;
-       }
-       return 0;
-}
-
-static int scst_proc_sgv_read(char *buffer, char **start,
-       off_t offset, int length, int *eof, void *data)
-{
-       int res = 0;
-       int size;
-       struct scst_proc_update_struct st;
-
-       TRACE_ENTRY();
-
-       TRACE_DBG("offset: %d, length %d", (int) offset, length);
-
-       memset(&st, 0, sizeof(st));
-
-       size = scnprintf(buffer + st.len, length - st.len, "%-20s %-11s %-11s",
-               "Name", "Hit", "Total");
-       if (scst_proc_update_size(size, offset, length, &st))
-               goto stop_output;
-
-       if (scst_proc_sgv_read_1(buffer, offset, length, &st, &scst_sgv.norm,
-                       "sgv"))
-               goto stop_output;
-
-       if (scst_proc_sgv_read_1(buffer, offset, length, &st,
-                       &scst_sgv.norm_clust, "sgv-clust"))
-               goto stop_output;
-
-       if (scst_proc_sgv_read_1(buffer, offset, length, &st,
-                       &scst_sgv.dma, "sgv-dma"))
-               goto stop_output;
-
-#ifdef SCST_HIGHMEM
-       if (scst_proc_sgv_read_1(buffer, offset, length, &st,
-                       &scst_sgv.highmem, "sgv-highmem"))
-               goto stop_output;
-
-#endif
-
-       size = scnprintf(buffer + st.len, length - st.len, "\n%-32s %-11d\n", 
-               "big", atomic_read(&sgv_big_total_alloc));
-       if (scst_proc_update_size(size, offset, length, &st))
-               goto stop_output;
-
-       size = scnprintf(buffer + st.len, length - st.len, "\n%-32s %-11d\n", 
-               "other", atomic_read(&sgv_other_total_alloc));
-       if (scst_proc_update_size(size, offset, length, &st))
-               goto stop_output;
-
-       *eof = 1;
-
-stop_output:
-       *start = buffer + (offset - st.begin);
-       st.len -= (offset - st.begin);
-       if (st.len > length)
-               st.len = length;
-       res = max(0, st.len);
-
-       TRACE_EXIT_RES(res);
-       return res;
-}
-
-static int scst_proc_init_sgv(void)
+static int __init scst_proc_init_sgv(void)
 {
        int res = 0;
        struct proc_dir_entry *pr;
 
        TRACE_ENTRY();
 
-       pr = create_proc_read_entry("sgv", S_IFREG | S_IRUGO,
-                scst_proc_scsi_tgt, scst_proc_sgv_read, NULL);
+       pr = scst_create_proc_entry(scst_proc_scsi_tgt, "sgv", &scst_sgv_proc_data);
        if (pr == NULL) {
                PRINT_ERROR_PR("%s", "cannot create sgv /proc entry");
                res = -ENOMEM;
@@ -864,14 +607,14 @@ static int scst_proc_init_sgv(void)
        return res;
 }
 
-static void scst_proc_cleanup_sgv(void)
+static void __exit scst_proc_cleanup_sgv(void)
 {
        TRACE_ENTRY();
        remove_proc_entry("sgv", scst_proc_scsi_tgt);
        TRACE_EXIT();
 }
 
-int scst_proc_init_module(void)
+int __init scst_proc_init_module(void)
 {
        int res = 0;
        struct proc_dir_entry *generic;
@@ -884,57 +627,47 @@ int scst_proc_init_module(void)
                goto out_nomem;
        }
 
-       generic = create_proc_read_entry(SCST_PROC_ENTRY_NAME,
-                                        S_IFREG | S_IRUGO | S_IWUSR,
-                                        scst_proc_scsi_tgt,
-                                        scst_scsi_tgt_proc_info, NULL);
+       generic = scst_create_proc_entry(scst_proc_scsi_tgt,
+                                        SCST_PROC_ENTRY_NAME,
+                                        &scst_tgt_proc_data);
        if (!generic) {
                PRINT_ERROR_PR("cannot init /proc/%s/%s",
                            SCST_PROC_ENTRY_NAME, SCST_PROC_ENTRY_NAME);
                goto out_remove;
        }
-       generic->write_proc = scst_proc_scsi_tgt_gen_write;
 
-       generic = create_proc_read_entry(SCST_PROC_VERSION_NAME,
-                                        S_IFREG | S_IRUGO,
-                                        scst_proc_scsi_tgt,
-                                        scst_proc_version_read, NULL);
+       generic = scst_create_proc_entry(scst_proc_scsi_tgt, SCST_PROC_VERSION_NAME,
+                                        &scst_version_proc_data);
        if (!generic) {
                PRINT_ERROR_PR("cannot init /proc/%s/%s",
                            SCST_PROC_ENTRY_NAME, SCST_PROC_VERSION_NAME);
                goto out_remove1;
        }
 
-       generic = create_proc_read_entry(SCST_PROC_SESSIONS_NAME,
-                                        S_IFREG | S_IRUGO,
-                                        scst_proc_scsi_tgt,
-                                        scst_proc_sessions_read, NULL);
+       generic = scst_create_proc_entry(scst_proc_scsi_tgt, SCST_PROC_SESSIONS_NAME,
+                                        &scst_sessions_proc_data);
        if (!generic) {
                PRINT_ERROR_PR("cannot init /proc/%s/%s",
                            SCST_PROC_ENTRY_NAME, SCST_PROC_SESSIONS_NAME);
                goto out_remove2;
        }
 
-       generic = create_proc_read_entry(SCST_PROC_HELP_NAME,
-                                        S_IFREG | S_IRUGO,
-                                        scst_proc_scsi_tgt,
-                                        scst_proc_help_read, NULL);
+       generic = scst_create_proc_entry(scst_proc_scsi_tgt, SCST_PROC_HELP_NAME,
+                                        &scst_help_proc_data);
        if (!generic) {
                PRINT_ERROR_PR("cannot init /proc/%s/%s",
                            SCST_PROC_ENTRY_NAME, SCST_PROC_HELP_NAME);
                goto out_remove3;
        }
 
-       generic = create_proc_read_entry(SCST_PROC_THREADS_NAME,
-                                        S_IFREG | S_IRUGO | S_IWUSR,
-                                        scst_proc_scsi_tgt,
-                                        scst_proc_threads_read, NULL);
+       generic = scst_create_proc_entry(scst_proc_scsi_tgt,
+                                        SCST_PROC_THREADS_NAME,
+                                        &scst_threads_proc_data);
        if (!generic) {
                PRINT_ERROR_PR("cannot init /proc/%s/%s",
                            SCST_PROC_ENTRY_NAME, SCST_PROC_THREADS_NAME);
                goto out_remove4;
        }
-       generic->write_proc = scst_proc_threads_write;
 
        if (scst_proc_init_module_log() < 0) {
                goto out_remove5;
@@ -981,7 +714,7 @@ out_nomem:
        goto out;
 }
 
-void scst_proc_cleanup_module(void)
+void __exit scst_proc_cleanup_module(void)
 {
        TRACE_ENTRY();
 
@@ -999,188 +732,12 @@ void scst_proc_cleanup_module(void)
        TRACE_EXIT();
 }
 
-static int scst_scsi_tgt_proc_info(char *buffer, char **start,
-                                  off_t offset, int length, int *eof,
-                                  void *data)
-{
-       int res = 0;
-       int size, len = 0, plen, pplen;
-       off_t begin = 0, pos = 0, pbegin, ppbegin;
-       struct scst_device *dev;
-
-       TRACE_ENTRY();
-
-//TRACE(TRACE_SPECIAL, "offset=%ld, length=%d", offset, length);
-
-       if (down_interruptible(&scst_mutex) != 0) {
-               res = -EINTR;
-               goto out;
-       }
-
-       size = scnprintf(buffer + len, length - len, "%-60s%s\n",
-                      "Device (host:ch:id:lun or name)",
-                      "Device handler");
-
-//TRACE(TRACE_SPECIAL, "size=%d, pos=%ld, begin=%ld, len=%d, buf %s",
-//     size, pos, begin, len, buffer+len);
-
-       if (size > 0) {
-               len += size;
-               pos = begin + len;
-               if (pos <= offset) {
-                       len = 0;
-                       begin = pos;
-               } else if (pos >= offset + length)
-                       goto stop_output;
-       } else
-               goto stop_output;
-
-       ppbegin = pbegin = begin;
-       pplen = plen = len;
-       list_for_each_entry(dev, &scst_dev_list, dev_list_entry) {
-               if (dev->virt_id == 0) {
-                       char conv[12];
-                       size = scnprintf(buffer + len, length - len,
-                                      "%d:%d:%d:",
-                                       dev->scsi_dev->host->host_no,
-                                       dev->scsi_dev->channel,
-                                       dev->scsi_dev->id);
-                       sprintf(conv, "%%-%dd%%s\n", 60-size);
-                       size += scnprintf(buffer + len + size, length - len - size,
-                                       conv, dev->scsi_dev->lun,
-                                       dev->handler ? dev->handler->name : "-");
-               } else {
-                       size = scnprintf(buffer + len, length - len,
-                                      "%-60s%s\n",
-                                      dev->virt_name, dev->handler->name);
-               }
-
-//printk("size=%d, pbegin=%ld, plen=%d, ppbegin=%ld, pplen=%d, buf %s",
-//     size, pbegin, plen, ppbegin, pplen, buffer+len);
-
-               if (size > 0) {
-                       len += size;
-                       pos = begin + len;
-                       if (pos <= offset) {
-                               len = 0;
-                               begin = pos;
-                       }
-//TRACE(TRACE_SPECIAL, "pos=%ld, begin=%ld, len=%d", pos, begin, len);
-                       if (pos >= offset + length)
-                               goto stop_output;
-               } else {
-                       begin = ppbegin;
-                       len = pplen;
-                       goto stop_output;
-               }
-               ppbegin = pbegin;
-               pplen = plen;
-               pbegin = begin;
-               plen = len;
-       }
-
-       *eof = 1;
-
-stop_output:
-       *start = buffer + (offset - begin);
-       len -= (offset - begin);
-       if (len > length)
-               len = length;
-       res = max(0, len);
-
-//TRACE(TRACE_SPECIAL, "res=%d, start=%ld, len=%d, begin=%ld, eof=%d", res, offset-begin, len, begin, *eof);
-
-       up(&scst_mutex);
-
-out:
-       TRACE_EXIT_RES(res);
-       return res;
-}
-
-static int scst_proc_version_read(char *buffer, char **start,off_t offset,
-                                 int length, int *eof, void *data)
-{
-       int res;
-
-       TRACE_ENTRY();
-
-       res = scnprintf(buffer, length, "%s\n", SCST_VERSION_STRING);
-
-#ifdef STRICT_SERIALIZING
-       if (res < length)
-               res += scnprintf(&buffer[res], length-res, "Strict "
-                       "serializing enabled\n");
-#endif
-
-#ifdef EXTRACHECKS
-       if (res < length)
-               res += scnprintf(&buffer[res], length-res, "EXTRACHECKS\n");
-#endif
-
-#ifdef TRACING
-       if (res < length)
-               res += scnprintf(&buffer[res], length-res, "TRACING\n");
-#endif
-
-#ifdef DEBUG
-       if (res < length)
-               res += scnprintf(&buffer[res], length-res, "DEBUG\n");
-#endif
-
-#ifdef DEBUG_TM
-       if (res < length)
-               res += scnprintf(&buffer[res], length-res, "DEBUG_TM\n");
-#endif
-
-#ifdef DEBUG_RETRY
-       if (res < length)
-               res += scnprintf(&buffer[res], length-res, "DEBUG_RETRY\n");
-#endif
-
-#ifdef DEBUG_OOM
-       if (res < length)
-               res += scnprintf(&buffer[res], length-res, "DEBUG_OOM\n");
-#endif
-
-       TRACE_EXIT_RES(res);
-       return res;
-}
-
-static int scst_proc_help_read(char *buffer, char **start, off_t offset,
-                              int length, int *eof, void *data)
-{
-       int res;
-
-       TRACE_ENTRY();
-
-       res = scnprintf(buffer, length, "%s", scst_proc_help_string);
-
-       TRACE_EXIT_RES(res);
-       return res;
-}
-
-static int scst_proc_threads_read(char *buffer, char **start,off_t offset,
-                                  int length, int *eof, void *data)
-{
-       int res;
-
-       TRACE_ENTRY();
-
-       /* 2 mgmt threads */
-       res = scnprintf(buffer, length, "%d\n",
-               atomic_read(&scst_threads_count) - 2);
-
-       TRACE_EXIT_RES(res);
-       return res;
-}
-
-static int scst_proc_threads_write(struct file *file, const char *buf,
-                                  unsigned long length, void *data)
+static int scst_proc_threads_write(struct file *file, const char __user *buf,
+                                  size_t length, loff_t *off)
 {
        int res = length;
        int oldtn, newtn, delta;
        char *buffer;
-       
 
        TRACE_ENTRY();
 
@@ -1282,13 +839,13 @@ int scst_build_proc_target_entries(struct scst_tgt *vtt)
 
        TRACE_ENTRY();
 
-       if (vtt->tgtt->proc_info) {
+       if (vtt->tgtt->read_proc || vtt->tgtt->write_proc) {
                /* create the proc file entry for the device */
                scnprintf(name, sizeof(name), "%d", vtt->tgtt->proc_dev_num);
-               p = create_proc_read_entry(name, S_IFREG | S_IRUGO | S_IWUSR,
-                                          vtt->tgtt->proc_tgt_root,
-                                          scst_proc_scsi_tgt_read,
-                                          (void *)vtt);
+               scst_scsi_tgt_proc_data.data = (void*)vtt;
+               p = scst_create_proc_entry(vtt->tgtt->proc_tgt_root,
+                                          name,
+                                          &scst_scsi_tgt_proc_data);
                if (p == NULL) {
                        PRINT_ERROR_PR("Not enough memory to register SCSI "
                             "target entry %s in /proc/%s/%s", name,
@@ -1296,7 +853,6 @@ int scst_build_proc_target_entries(struct scst_tgt *vtt)
                        res = -ENOMEM;
                        goto out;
                }
-               p->write_proc = scst_proc_scsi_tgt_write;
                vtt->proc_num = vtt->tgtt->proc_dev_num;
                vtt->tgtt->proc_dev_num++;
        }
@@ -1312,7 +868,7 @@ void scst_cleanup_proc_target_entries(struct scst_tgt *vtt)
 
        TRACE_ENTRY();
 
-       if (vtt->tgtt->proc_info) {
+       if (vtt->tgtt->read_proc || vtt->tgtt->write_proc) {
                scnprintf(name, sizeof(name), "%d", vtt->proc_num);
                remove_proc_entry(name, vtt->tgtt->proc_tgt_root);
        }
@@ -1321,39 +877,10 @@ void scst_cleanup_proc_target_entries(struct scst_tgt *vtt)
        return;
 }
 
-static int scst_proc_scsi_tgt_read(char *buffer, char **start,
-                                  off_t offset, int length, int *eof,
-                                  void *data)
-{
-       struct scst_tgt *vtt = data;
-       int res = 0;
-
-       TRACE_ENTRY();
-
-       TRACE_DBG("offset: %d, length %d, id: %d",
-             (int) offset, length, vtt->proc_num);
-
-       if (down_interruptible(&scst_proc_mutex) != 0) {
-               res = -EINTR;
-               goto out;
-       }
-
-       if (vtt->tgtt->proc_info) {
-               res = vtt->tgtt->proc_info(buffer, start, offset, length, eof,
-                   vtt, 0);
-       }
-
-       up(&scst_proc_mutex);
-
-out:
-       TRACE_EXIT_RES(res);
-       return res;
-}
-
-static int scst_proc_scsi_tgt_write(struct file *file, const char *buf,
-                                   unsigned long length, void *data)
+static int scst_proc_scsi_tgt_write(struct file *file, const char __user *buf,
+                                   size_t length, loff_t *off)
 {
-       struct scst_tgt *vtt = data;
+       struct scst_tgt *vtt = (struct scst_tgt *)PDE(file->f_dentry->d_inode)->data;
        ssize_t res = 0;
        char *buffer;
        char *start;
@@ -1361,7 +888,7 @@ static int scst_proc_scsi_tgt_write(struct file *file, const char *buf,
 
        TRACE_ENTRY();
 
-       if (vtt->tgtt->proc_info == NULL) {
+       if (vtt->tgtt->write_proc == NULL) {
                res = -ENOSYS;
                goto out;
        }
@@ -1397,7 +924,7 @@ static int scst_proc_scsi_tgt_write(struct file *file, const char *buf,
                goto out_free;
        }
 
-       res = vtt->tgtt->proc_info(buffer, &start, 0, length, &eof, vtt, 1);
+       res = vtt->tgtt->write_proc(buffer, &start, 0, length, &eof, vtt);
 
        up(&scst_proc_mutex);
 
@@ -1427,11 +954,10 @@ int scst_build_proc_dev_handler_dir_entries(struct scst_dev_type *dev_type)
                goto out_nomem;
        }
 
-       p = create_proc_read_entry(SCST_PROC_DEV_HANDLER_TYPE_ENTRY_NAME,
-                                  S_IFREG | S_IRUGO,
-                                  dev_type->proc_dev_type_root,
-                                  scst_proc_scsi_dev_handler_type_read,
-                                  (void *)dev_type);
+       scst_dev_handler_type_proc_data.data = dev_type;
+       p = scst_create_proc_entry(dev_type->proc_dev_type_root,
+                                  SCST_PROC_DEV_HANDLER_TYPE_ENTRY_NAME,
+                                  &scst_dev_handler_type_proc_data);
        if (p == NULL) {
                PRINT_ERROR_PR("Not enough memory to register dev "
                     "handler entry %s in /proc/%s/%s",
@@ -1440,20 +966,18 @@ int scst_build_proc_dev_handler_dir_entries(struct scst_dev_type *dev_type)
                goto out_remove;
        }
 
-       if (dev_type->proc_info) {
+       if (dev_type->read_proc || dev_type->write_proc) {
                /* create the proc file entry for the dev type handler */
-               p = create_proc_read_entry(dev_type->name,
-                                          S_IFREG | S_IRUGO | S_IWUSR,
-                                          dev_type->proc_dev_type_root,
-                                          scst_proc_scsi_dev_handler_read,
-                                          (void *)dev_type);
+               scst_dev_handler_proc_data.data = (void *)dev_type;
+               p = scst_create_proc_entry(dev_type->proc_dev_type_root,
+                                          dev_type->name,
+                                          &scst_dev_handler_proc_data);
                if (p == NULL) {
                        PRINT_ERROR_PR("Not enough memory to register dev "
                             "handler entry %s in /proc/%s/%s", dev_type->name,
                             SCST_PROC_ENTRY_NAME, dev_type->name);
                        goto out_remove1;
                }
-               p->write_proc = scst_proc_scsi_dev_handler_write;
        }
 
 out:
@@ -1479,7 +1003,7 @@ void scst_cleanup_proc_dev_handler_dir_entries(struct scst_dev_type *dev_type)
        if (dev_type->proc_dev_type_root) {
                remove_proc_entry(SCST_PROC_DEV_HANDLER_TYPE_ENTRY_NAME,
                                  dev_type->proc_dev_type_root);
-               if (dev_type->proc_info) {
+               if (dev_type->read_proc || dev_type->write_proc) {
                        remove_proc_entry(dev_type->name,
                                          dev_type->proc_dev_type_root);
                }
@@ -1491,56 +1015,10 @@ void scst_cleanup_proc_dev_handler_dir_entries(struct scst_dev_type *dev_type)
        return;
 }
 
-static int scst_proc_scsi_dev_handler_type_read(char *buffer, char **start,
-                                               off_t offset, int length,
-                                               int *eof, void *data)
-{
-       struct scst_dev_type *dev_type = data;
-       int n = 0;
-
-       TRACE_ENTRY();
-
-       n = scnprintf(buffer, length, "%d - %s\n", dev_type->type,
-                   dev_type->type > 0x0f ?
-                   "unknown" : scst_proc_dev_handler_type[dev_type->type]);
-
-       TRACE_EXIT_RES(n);
-       return n;
-}
-
-static int scst_proc_scsi_dev_handler_read(char *buffer, char **start,
-                                          off_t offset, int length, int *eof,
-                                          void *data)
-{
-       struct scst_dev_type *dev_type = data;
-       int res = 0;
-
-       TRACE_ENTRY();
-
-       TRACE_DBG("offset: %d, length %d, name: %s",
-             (int) offset, length, dev_type->name);
-
-       if (down_interruptible(&scst_proc_mutex) != 0) {
-               res = -EINTR;
-               goto out;
-       }
-
-       if (dev_type->proc_info) {
-               res = dev_type->proc_info(buffer, start, offset, length, eof,
-                   dev_type, 0);
-       }
-
-       up(&scst_proc_mutex);
-
-out:
-       TRACE_EXIT_RES(res);
-       return res;
-}
-
-static int scst_proc_scsi_dev_handler_write(struct file *file, const char *buf,
-                                           unsigned long length, void *data)
+static int scst_proc_scsi_dev_handler_write(struct file *file, const char __user *buf,
+                                           size_t length, loff_t *off)
 {
-       struct scst_dev_type *dev_type = data;
+       struct scst_dev_type *dev_type = (struct scst_dev_type *)PDE(file->f_dentry->d_inode)->data;
        ssize_t res = 0;
        char *buffer;
        char *start;
@@ -1548,7 +1026,7 @@ static int scst_proc_scsi_dev_handler_write(struct file *file, const char *buf,
 
        TRACE_ENTRY();
 
-       if (dev_type->proc_info == NULL) {
+       if (dev_type->write_proc == NULL) {
                res = -ENOSYS;
                goto out;
        }
@@ -1585,7 +1063,7 @@ static int scst_proc_scsi_dev_handler_write(struct file *file, const char *buf,
                goto out_free;
        }
 
-       res = dev_type->proc_info(buffer, &start, 0, length, &eof, dev_type, 1);
+       res = dev_type->write_proc(buffer, &start, 0, length, &eof, dev_type);
 
        up(&scst_proc_mutex);
 
@@ -1597,9 +1075,8 @@ out:
        return res;
 }
 
-static int scst_proc_scsi_tgt_gen_write(struct file *file,
-                                       const char *buf,
-                                       unsigned long length, void *data)
+static int scst_proc_scsi_tgt_gen_write(struct file *file, const char __user *buf,
+                                       size_t length, loff_t *off)
 {
        int res = length, rc = 0, action;
        char *buffer, *p;
@@ -1814,108 +1291,20 @@ out_synt_err:
        goto out;
 }
 
-static int scst_proc_groups_devices_read(char *buffer, char **start,
-                                        off_t offset, int length, int *eof,
-                                        void *data)
+static int scst_proc_groups_devices_write(struct file *file, const char __user *buf,
+                                         size_t length, loff_t *off)
 {
-       int res = 0;
-       struct scst_acg *acg = (struct scst_acg *)data;
-       struct scst_acg_dev *acg_dev;
-       int size, len = 0, plen, pplen;
-       off_t begin = 0, pos = 0, pbegin, ppbegin;
+       int res = length, action, virt = 0, rc, read_only = 0;
+       char *buffer, *p, *e = NULL;
+       int host, channel = 0, id = 0, lun = 0, virt_lun;
+       struct scst_acg *acg = (struct scst_acg *)PDE(file->f_dentry->d_inode)->data;
+       struct scst_acg_dev *acg_dev = NULL, *acg_dev_tmp;
+       struct scst_device *d, *dev = NULL;
 
        TRACE_ENTRY();
 
-       if (down_interruptible(&scst_mutex) != 0) {
-               res = -EINTR;
-               goto out;
-       }
-
-       size = scnprintf(buffer + len, length - len, "%-60s%s  %s\n",
-                      "Device (host:ch:id:lun or name)",
-                      "Virtual lun", "Options");
-       if (size > 0) {
-               len += size;
-               pos = begin + len;
-               if (pos <= offset) {
-                       len = 0;
-                       begin = pos;
-               } else if (pos >= offset + length)
-                       goto stop_output;
-       } else
-               goto stop_output;
-
-       ppbegin = pbegin = begin;
-       pplen = plen = len;
-       list_for_each_entry(acg_dev, &acg->acg_dev_list, acg_dev_list_entry) {
-               if (acg_dev->dev->virt_id == 0) {
-                       char conv[20];
-                       size = scnprintf(buffer + len, length - len,
-                                       "%d:%d:%d:",
-                                       acg_dev->dev->scsi_dev->host->host_no,
-                                       acg_dev->dev->scsi_dev->channel,
-                                       acg_dev->dev->scsi_dev->id);
-                       sprintf(conv, "%%-%dd%%4d%%12s\n", 60-size);
-                       size += scnprintf(buffer + len + size,
-                                       length - len - size, conv,
-                                       acg_dev->dev->scsi_dev->lun,
-                                       acg_dev->lun,
-                                       acg_dev->rd_only_flag ? "RO" : "");
-               } else {
-                       size = scnprintf(buffer + len, length - len,
-                                      "%-60s%4d%12s\n",
-                                      acg_dev->dev->virt_name, acg_dev->lun,
-                                      acg_dev->rd_only_flag ? "RO" : "");
-               }
-               if (size > 0) {
-                       len += size;
-                       pos = begin + len;
-                       if (pos <= offset) {
-                               len = 0;
-                               begin = pos;
-                       } else if (pos >= offset + length)
-                               goto stop_output;
-               } else {
-                       begin = ppbegin;
-                       len = pplen;
-                       goto stop_output;
-               }
-               ppbegin = pbegin;
-               pplen = plen;
-               pbegin = begin;
-               plen = len;
-       }
-
-       *eof = 1;
-
-stop_output:
-       *start = buffer + (offset - begin);
-       len -= (offset - begin);
-       if (len > length)
-               len = length;
-       res = max(0, len);
-
-       up(&scst_mutex);
-
-out:
-       TRACE_EXIT_RES(res);
-       return res;
-}
-
-static int scst_proc_groups_devices_write(struct file *file, const char *buf,
-                                         unsigned long length, void *data)
-{
-       int res = length, action, virt = 0, rc, read_only = 0;
-       char *buffer, *p, *e = NULL;
-       int host, channel = 0, id = 0, lun = 0, virt_lun;
-       struct scst_acg *acg = (struct scst_acg *)data;
-       struct scst_acg_dev *acg_dev = NULL, *acg_dev_tmp;
-       struct scst_device *d, *dev = NULL;
-
-       TRACE_ENTRY();
-
-       if (length > SCST_PROC_BLOCK_SIZE) {
-               res = -EOVERFLOW;
+       if (length > SCST_PROC_BLOCK_SIZE) {
+               res = -EOVERFLOW;
                goto out;
        }
        if (!buf) {
@@ -2090,146 +1479,12 @@ out:
        return res;
 }
 
-static int scst_proc_sessions_read(char *buffer, char **start,
-                                        off_t offset, int length, int *eof,
-                                        void *data)
-{
-       int res = 0;
-       struct scst_acg *acg;
-       struct scst_session *sess;
-       int size, len = 0, plen, pplen;
-       off_t begin = 0, pos = 0,  pbegin, ppbegin;
-
-       TRACE_ENTRY();
-
-       if (down_interruptible(&scst_mutex) != 0) {
-               res = -EINTR;
-               goto out;
-       }
-
-       size = scnprintf(buffer + len, length - len, "%-20s%-35s%-20s%-15s\n",
-                      "Target name", "Initiator name", "Group name", 
-                      "Command Count");
-       if (size > 0) {
-               len += size;
-               pos = begin + len;
-               if (pos <= offset) {
-                       len = 0;
-                       begin = pos;
-               } else if (pos >= offset + length)
-                       goto stop_output;
-       } else
-               goto stop_output;
-
-       ppbegin = pbegin = begin;
-       pplen = plen = len;
-       list_for_each_entry(acg, &scst_acg_list, scst_acg_list_entry) {
-               list_for_each_entry(sess, &acg->acg_sess_list, acg_sess_list_entry) {
-                       size = scnprintf(buffer + len, length - len,
-                                      "%-20s%-35s%-20s%-15d\n",
-                                       sess->tgt->tgtt->name,
-                                       sess->initiator_name,
-                                      acg->acg_name,
-                                      sess->sess_cmd_count);
-                       if (size > 0) {
-                               len += size;
-                               pos = begin + len;
-                               if (pos <= offset) {
-                                       len = 0;
-                                       begin = pos;
-                               } else if (pos >= offset + length)
-                                       goto stop_output;
-                       } else {
-                               begin = ppbegin;
-                               len = pplen;
-                               goto stop_output;
-                       }
-                       ppbegin = pbegin;
-                       pplen = plen;
-                       pbegin = begin;
-                       plen = len;
-               }
-       }
-
-       *eof = 1;
-
-stop_output:
-       *start = buffer + (offset - begin);
-       len -= (offset - begin);
-       if (len > length)
-               len = length;
-       res = max(0, len);
-
-       up(&scst_mutex);
-
-out:
-       TRACE_EXIT_RES(res);
-       return res;
-}
-
-static int scst_proc_groups_names_read(char *buffer, char **start,
-                                      off_t offset, int length, int *eof,
-                                      void *data)
-{
-       int res = 0;
-       struct scst_acg *acg = (struct scst_acg *)data;
-       struct scst_acn *name;
-       int size, len = 0, plen, pplen;
-       off_t begin = 0, pos = 0, pbegin, ppbegin;
-
-       TRACE_ENTRY();
-
-       if (down_interruptible(&scst_mutex) != 0) {
-               res = -EINTR;
-               goto out;
-       }
-
-       ppbegin = pbegin = begin;
-       pplen = plen = len;
-       list_for_each_entry(name, &acg->acn_list, acn_list_entry) {
-               size = scnprintf(buffer + len, length - len, "%s\n",
-                       name->name);
-               if (size > 0) {
-                       len += size;
-                       pos = begin + len;
-                       if (pos <= offset) {
-                               len = 0;
-                               begin = pos;
-                       } else if (pos >= offset + length)
-                               goto stop_output;
-               } else {
-                       begin = ppbegin;
-                       len = pplen;
-                       goto stop_output;
-               }
-               ppbegin = pbegin;
-               pplen = plen;
-               pbegin = begin;
-               plen = len;
-       }
-
-       *eof = 1;
-
-stop_output:
-       *start = buffer + (offset - begin);
-       len -= (offset - begin);
-       if (len > length)
-               len = length;
-       res = max(0, len);
-
-       up(&scst_mutex);
-
-out:
-       TRACE_EXIT_RES(res);
-       return res;
-}
-
-static int scst_proc_groups_names_write(struct file *file, const char *buf,
-                                       unsigned long length, void *data)
+static int scst_proc_groups_names_write(struct file *file, const char __user *buf,
+                                       size_t length, loff_t *off)
 {
        int res = length, action;
        char *buffer, *p, *e;
-       struct scst_acg *acg = (struct scst_acg *)data;
+       struct scst_acg *acg = (struct scst_acg *)PDE(file->f_dentry->d_inode)->data;
        struct scst_acn *n, *nn;
 
        TRACE_ENTRY();
@@ -2324,3 +1579,445 @@ out:
        TRACE_EXIT_RES(res);
        return res;
 }
+
+static int scst_version_info_show(struct seq_file *seq, void *v)
+{
+       TRACE_ENTRY();
+
+       seq_printf(seq, "%s\n", SCST_VERSION_STRING);
+
+#ifdef STRICT_SERIALIZING
+       seq_printf(seq, "Strict serializing enabled\n");
+#endif
+
+#ifdef EXTRACHECKS
+       seq_printf(seq, "EXTRACHECKS\n");
+#endif
+
+#ifdef TRACING
+       seq_printf(seq, "TRACING\n");
+#endif
+
+#ifdef DEBUG
+       seq_printf(seq, "DEBUG\n");
+#endif
+
+#ifdef DEBUG_TM
+       seq_printf(seq, "DEBUG_TM\n");
+#endif
+
+#ifdef DEBUG_RETRY
+       seq_printf(seq, "DEBUG_RETRY\n");
+#endif
+
+#ifdef DEBUG_OOM
+       seq_printf(seq, "DEBUG_OOM\n");
+#endif
+
+       TRACE_EXIT();
+       return 0;
+}
+
+static struct scst_proc_data scst_version_proc_data = {
+       SCST_DEF_RW_SEQ_OP(NULL)
+       .show = scst_version_info_show,
+};
+
+static int scst_help_info_show(struct seq_file *seq, void *v)
+{
+       TRACE_ENTRY();
+
+       seq_printf(seq, "%s\n", scst_proc_help_string);
+
+       TRACE_EXIT();
+       return 0;
+}
+
+static struct scst_proc_data scst_help_proc_data = {
+       SCST_DEF_RW_SEQ_OP(NULL)
+       .show = scst_help_info_show,
+};
+
+static int scst_dev_handler_type_info_show(struct seq_file *seq, void *v)
+{
+       struct scst_dev_type *dev_type = (struct scst_dev_type *)seq->private;
+
+       TRACE_ENTRY();
+
+       seq_printf(seq, "%d - %s\n", dev_type->type,
+                   dev_type->type > ARRAY_SIZE(scst_proc_dev_handler_type) ?
+                   "unknown" : scst_proc_dev_handler_type[dev_type->type]);
+
+       TRACE_EXIT();
+       return 0;
+}
+
+static struct scst_proc_data scst_dev_handler_type_proc_data = {
+       SCST_DEF_RW_SEQ_OP(NULL)
+       .show = scst_dev_handler_type_info_show,
+};
+
+static int scst_sessions_info_show(struct seq_file *seq, void *v)
+{
+       int res = 0;
+       struct scst_acg *acg;
+       struct scst_session *sess;
+
+       TRACE_ENTRY();
+
+       if (down_interruptible(&scst_mutex) != 0) {
+               res = -EINTR;
+               goto out;
+       }
+
+       seq_printf(seq, "%-20s%-35s%-20s%-15s\n", "Target name", "Initiator name", 
+                      "Group name", "Command Count");
+
+       list_for_each_entry(acg, &scst_acg_list, scst_acg_list_entry) {
+               list_for_each_entry(sess, &acg->acg_sess_list, acg_sess_list_entry) {
+                       seq_printf(seq, "%-20s%-35s%-20s%-15d\n",
+                                       sess->tgt->tgtt->name,
+                                       sess->initiator_name,
+                                       acg->acg_name,
+                                       sess->sess_cmd_count);
+               }
+       }
+
+       up(&scst_mutex);
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+static struct scst_proc_data scst_sessions_proc_data = {
+       SCST_DEF_RW_SEQ_OP(NULL)
+       .show = scst_sessions_info_show,
+};
+
+static int scst_do_sgv_read(struct seq_file *seq, const struct sgv_pool *pool, const char *name)
+{
+       int i;
+
+       seq_printf(seq, "\n%-20s %-11d %-11d\n", name, atomic_read(&pool->acc.hit_alloc),
+               atomic_read(&pool->acc.total_alloc));
+
+       for (i = 0; i < SGV_POOL_ELEMENTS; i++) {
+               seq_printf(seq, "  %-18s %-11d %-11d\n", pool->cache_names[i], 
+                       atomic_read(&pool->cache_acc[i].hit_alloc),
+                       atomic_read(&pool->cache_acc[i].total_alloc));
+       }
+       return 0;
+}
+
+static int scst_sgv_info_show(struct seq_file *seq, void *v)
+{
+       TRACE_ENTRY();
+
+       seq_printf(seq, "%-20s %-11s %-11s", "Name", "Hit", "Total");
+
+       scst_do_sgv_read(seq, &scst_sgv.norm, "sgv");
+       scst_do_sgv_read(seq, &scst_sgv.norm_clust, "sgv-clust");
+       scst_do_sgv_read(seq, &scst_sgv.dma, "sgv-dma");
+
+#ifdef SCST_HIGHMEM
+       scst_do_sgv_read(seq, &scst_sgv.highmem, "sgv-highmem");
+#endif
+
+       seq_printf(seq, "\n%-32s %-11d\n", "big", atomic_read(&sgv_big_total_alloc));
+       seq_printf(seq, "%-32s %-11d\n", "other", atomic_read(&sgv_other_total_alloc));
+
+       TRACE_EXIT();
+       return 0;
+}
+
+static struct scst_proc_data scst_sgv_proc_data = {
+       SCST_DEF_RW_SEQ_OP(NULL)
+       .show = scst_sgv_info_show,
+};
+
+static int scst_groups_names_show(struct seq_file *seq, void *v)
+{
+       int res = 0;
+        struct scst_acg *acg = (struct scst_acg *)seq->private;
+       struct scst_acn *name;
+
+       TRACE_ENTRY();
+
+       if (down_interruptible(&scst_mutex) != 0) {
+               res = -EINTR;
+               goto out;
+       }
+
+       list_for_each_entry(name, &acg->acn_list, acn_list_entry) {
+               seq_printf(seq, "%s\n", name->name);
+       }
+
+       up(&scst_mutex);
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+static struct scst_proc_data scst_groups_names_proc_data = {
+       SCST_DEF_RW_SEQ_OP(scst_proc_groups_names_write)
+       .show = scst_groups_names_show,
+};
+
+static int scst_groups_devices_show(struct seq_file *seq, void *v)
+{
+       int res = 0;
+       struct scst_acg *acg = (struct scst_acg *)seq->private;
+       struct scst_acg_dev *acg_dev;
+
+       TRACE_ENTRY();
+
+       if (down_interruptible(&scst_mutex) != 0) {
+               res = -EINTR;
+               goto out;
+       }
+
+       seq_printf(seq, "%-60s%s  %s\n", "Device (host:ch:id:lun or name)",
+                      "Virtual lun", "Options");
+
+       list_for_each_entry(acg_dev, &acg->acg_dev_list, acg_dev_list_entry) {
+               if (acg_dev->dev->virt_id == 0) {
+                       char conv[60];
+                       int size = sizeof(conv);
+
+                       memset(conv, 0, size);
+                       size = snprintf(conv, size, "%d:%d:%d:",
+                                       acg_dev->dev->scsi_dev->host->host_no,
+                                       acg_dev->dev->scsi_dev->channel,
+                                       acg_dev->dev->scsi_dev->id);
+                       seq_printf(seq, "%s", conv);
+                       sprintf(conv, "%%-%dd%%4d%%12s\n", 60 - size);
+                       seq_printf(seq, conv,
+                                       acg_dev->dev->scsi_dev->lun,
+                                       acg_dev->lun,
+                                       acg_dev->rd_only_flag ? "RO" : "");
+               } else {
+                       seq_printf(seq, "%-60s%4d%12s\n",
+                                      acg_dev->dev->virt_name, acg_dev->lun,
+                                      acg_dev->rd_only_flag ? "RO" : "");
+               }
+       }
+       up(&scst_mutex);
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+static struct scst_proc_data scst_groups_devices_proc_data = {
+       SCST_DEF_RW_SEQ_OP(scst_proc_groups_devices_write)
+       .show = scst_groups_devices_show,
+};
+
+#if defined(DEBUG) || defined(TRACING)
+
+int scst_proc_read_tlb(const struct scst_proc_log *tbl, struct seq_file *seq, 
+       unsigned long log_level, int *first)
+{
+       const struct scst_proc_log *t = tbl;
+       int res = 0;
+
+       while (t->token) {
+               if (log_level & t->val) {
+                       seq_printf(seq, "%s%s", *first ? "" : " | ", t->token);
+                       *first = 0;
+               }
+               t++;
+       }
+       return res;
+}
+
+int scst_proc_log_entry_read(struct seq_file *seq, unsigned long log_level,
+                            const struct scst_proc_log *tbl)
+{
+       int res = 0, first = 1;
+
+       TRACE_ENTRY();
+
+       scst_proc_read_tlb(scst_proc_trace_tbl, seq, log_level, &first);
+
+       if (tbl) {
+               scst_proc_read_tlb(tbl, seq, log_level, &first);
+       }
+
+       seq_printf(seq, "%s\n", first ? "none" : "");
+
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+static int log_info_show(struct seq_file *seq, void *v)
+{
+       int res;
+
+       TRACE_ENTRY();
+
+       if (down_interruptible(&scst_proc_mutex) != 0) {
+               res = -EINTR;
+               goto out;
+       }
+
+       res = scst_proc_log_entry_read(seq, trace_flag, scst_proc_local_trace_tbl);
+
+       up(&scst_proc_mutex);
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+static struct scst_proc_data scst_log_proc_data = {
+       SCST_DEF_RW_SEQ_OP(scst_proc_scsi_tgt_gen_write_log)
+       .show = log_info_show,
+       .data = "scsi_tgt",
+};
+
+#endif
+
+static int scst_tgt_info_show(struct seq_file *seq, void *v)
+{
+       int res = 0;
+       struct scst_device *dev;
+
+       TRACE_ENTRY();
+
+       if (down_interruptible(&scst_mutex) != 0) {
+               res = -EINTR;
+               goto out;
+       }
+
+       seq_printf(seq, "%-60s%s\n", "Device (host:ch:id:lun or name)", "Device handler");
+       list_for_each_entry(dev, &scst_dev_list, dev_list_entry) {
+               if (dev->virt_id == 0) {
+                       char conv[60];
+                       int size = sizeof(conv);
+                       size = snprintf(conv, size, "%d:%d:%d:",
+                                       dev->scsi_dev->host->host_no,
+                                       dev->scsi_dev->channel,
+                                       dev->scsi_dev->id);
+                       seq_printf(seq, "%s", conv);
+                       sprintf(conv, "%%-%dd%%s\n", 60 - size);
+                       seq_printf(seq, conv, dev->scsi_dev->lun,
+                                       dev->handler ? dev->handler->name : "-");
+               } else
+                       seq_printf(seq, "%-60s%s\n", dev->virt_name, dev->handler->name);
+       }
+
+       up(&scst_mutex);
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+static struct scst_proc_data scst_tgt_proc_data = {
+       SCST_DEF_RW_SEQ_OP(scst_proc_scsi_tgt_gen_write)
+       .show = scst_tgt_info_show,
+};
+
+static int scst_threads_info_show(struct seq_file *seq, void *v)
+{
+       TRACE_ENTRY();
+
+       /* 2 mgmt threads */
+       seq_printf(seq, "%d\n", atomic_read(&scst_threads_count) - 2);
+
+       TRACE_EXIT();
+       return 0;
+}
+
+static struct scst_proc_data scst_threads_proc_data = {
+       SCST_DEF_RW_SEQ_OP(scst_proc_threads_write)
+       .show = scst_threads_info_show,
+};
+
+static int scst_scsi_tgtinfo_show(struct seq_file *seq, void *v)
+{
+       struct scst_tgt *vtt = seq->private;
+       int res = 0;
+
+       TRACE_ENTRY();
+
+       if (down_interruptible(&scst_proc_mutex) != 0) {
+               res = -EINTR;
+               goto out;
+       }
+
+       if (vtt->tgtt->read_proc)
+               res = vtt->tgtt->read_proc(seq, vtt);
+
+       up(&scst_proc_mutex);
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+static struct scst_proc_data scst_scsi_tgt_proc_data = {
+       SCST_DEF_RW_SEQ_OP(scst_proc_scsi_tgt_write)
+       .show = scst_scsi_tgtinfo_show,
+};
+
+static int scst_dev_handler_info_show(struct seq_file *seq, void *v)
+{
+       struct scst_dev_type *dev_type = seq->private;
+       int res = 0;
+
+       TRACE_ENTRY();
+
+       if (down_interruptible(&scst_proc_mutex) != 0) {
+               res = -EINTR;
+               goto out;
+       }
+
+       if (dev_type->read_proc)
+               res = dev_type->read_proc(seq, dev_type);
+
+       up(&scst_proc_mutex);
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+static struct scst_proc_data scst_dev_handler_proc_data = {
+       SCST_DEF_RW_SEQ_OP(scst_proc_scsi_dev_handler_write)
+       .show = scst_dev_handler_info_show,
+};
+
+struct proc_dir_entry *scst_create_proc_entry(struct proc_dir_entry * root,
+       const char *name, struct scst_proc_data *pdata)
+{
+       struct proc_dir_entry *p = NULL;
+
+       TRACE_ENTRY();
+
+       if (root) {
+               mode_t mode;
+
+               mode  = S_IFREG | S_IRUGO | ((pdata->seq_op.write) ? S_IWUSR : 0);
+               p = create_proc_entry(name, mode, root);
+               if (p == NULL) {
+                       PRINT_ERROR_PR("Fail to create entry %s in /proc", name);
+               } else {
+                       p->proc_fops = &pdata->seq_op;
+                       p->data = pdata->data;
+               }
+       }
+
+       TRACE_EXIT();
+       return p;
+}
+
+int scst_single_seq_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, 
+               container_of(inode->i_fop, struct scst_proc_data, seq_op)->show, 
+               PDE(inode)->data);
+}
+