public inbox for linux-scsi@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] proper replacements for ->proc_info
@ 2003-04-23 19:21 Christoph Hellwig
  2003-04-23 19:29 ` James Bottomley
  0 siblings, 1 reply; 8+ messages in thread
From: Christoph Hellwig @ 2003-04-23 19:21 UTC (permalink / raw)
  To: James.Bottomley; +Cc: linux-scsi

Two new host template methods:

     int (* show_info)(struct Scsi_Host *, struct seq_file *);
     int (* store_info)(struct Scsi_Host *, const char *, size_t);

First one is used for reading of /proc/scsi/<driver>/<host>, second
for writing.  They get an explicit hosy pointer instead of the
integer number.  Read side outputs into the simple version of the
seq_file interface so lots of crappy string handling can go away,
write side isn't much different from the old version, just properly
split out.

->proc_info continues to work but I hope to move over all drivers
before 2.6.

scsi_debug and aic7xxx are the example drivers in this patch, more
will follow very soon.

btw, even this first patch already removes more code than it adds..


--- 1.58/drivers/scsi/hosts.h	Mon Mar 24 07:14:28 2003
+++ edited/drivers/scsi/hosts.h	Wed Apr 23 11:24:33 2003
@@ -63,13 +63,6 @@
     /* The pointer to the /proc/scsi directory entry */
     struct proc_dir_entry *proc_dir;
 
-    /* proc-fs info function.
-     * Can be used to export driver statistics and other infos to the world
-     * outside the kernel ie. userspace and it also provides an interface
-     * to feed the driver with information. Check eata_dma_proc.c for reference
-     */
-    int (*proc_info)(char *, char **, off_t, int, int, int);
-
     /*
      * The name pointer is a pointer to the name of the SCSI
      * device detected.
@@ -265,6 +258,23 @@
      */
     int (* bios_param)(struct scsi_device *, struct block_device *,
 		    sector_t, int []);
+
+    /*
+     * Show host information in /proc/scsi/<driver>/<host>.
+     * Optional.
+     */
+    int (* show_info)(struct Scsi_Host *, struct seq_file *);
+
+    /*
+     * Allow user-commands to be written to /proc/scsi/<driver>/<host>.
+     * Optional.
+     */
+    int (* store_info)(struct Scsi_Host *, const char *, size_t);
+
+    /*
+     * Obsolete interface version of the two above.
+     */
+    int (* proc_info)(char *, char **, off_t, int, int, int);
 
     /*
      * This determines if we will use a non-interrupt driven
--- 1.31/drivers/scsi/scsi_debug.c	Mon Mar 31 15:52:02 2003
+++ edited/drivers/scsi/scsi_debug.c	Wed Apr 23 12:20:46 2003
@@ -40,6 +40,7 @@
 #include <linux/smp_lock.h>
 #include <linux/vmalloc.h>
 #include <linux/moduleparam.h>
+#include <linux/seq_file.h>
 
 #include <linux/blk.h>
 #include "scsi.h"
@@ -1229,34 +1231,29 @@
 	return sdebug_info;
 }
 
-/* scsi_debug_proc_info
- * Used if the driver currently has no own support for /proc/scsi
- */
-static int scsi_debug_proc_info(char *buffer, char **start, off_t offset,
-				int length, int inode, int inout)
+static int scsi_debug_store_info(struct Scsi_Host *shost,
+				 const char *buffer, size_t length)
 {
-	int len, pos, begin;
-	int orig_length;
+	int minLen = length > 15 ? 15 : length, pos;
+	char arr[16];
 
-	orig_length = length;
+	if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
+		return -EACCES;
 
-	if (inout == 1) {
-		char arr[16];
-		int minLen = length > 15 ? 15 : length;
-
-		if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
-			return -EACCES;
-		memcpy(arr, buffer, minLen);
-		arr[minLen] = '\0';
-		if (1 != sscanf(arr, "%d", &pos))
-			return -EINVAL;
-		scsi_debug_opts = pos;
-		if (scsi_debug_every_nth > 0)
-                        scsi_debug_cmnd_count = 0;
-		return length;
-	}
-	begin = 0;
-	pos = len = sprintf(buffer, "scsi_debug adapter driver, %s\n"
+	memcpy(arr, buffer, minLen);
+	arr[minLen] = '\0';
+	if (1 != sscanf(arr, "%d", &pos))
+		return -EINVAL;
+	scsi_debug_opts = pos;
+	if (scsi_debug_every_nth > 0)
+                       scsi_debug_cmnd_count = 0;
+
+	return length;
+}
+
+static int scsi_debug_show_info(struct Scsi_Host *shost, struct seq_file *s)
+{
+	seq_printf(s, "scsi_debug adapter driver, %s\n"
 	    "num_tgts=%d, shared (ram) size=%d MB, opts=0x%x, "
 	    "every_nth=%d(curr:%d)\n"
 	    "delay=%d, max_luns=%d, scsi_level=%d\n"
@@ -1269,15 +1266,8 @@
 	    scsi_debug_max_luns, scsi_debug_scsi_level,
 	    SECT_SIZE, sdebug_cylinders_per, sdebug_heads, sdebug_sectors_per,
 	    num_aborts, num_dev_resets, num_bus_resets, num_host_resets);
-	if (pos < offset) {
-		len = 0;
-		begin = pos;
-	}
-	*start = buffer + (offset - begin);	/* Start of wanted data */
-	len -= (offset - begin);
-	if (len > length)
-		len = length;
-	return len;
+
+	return 0;
 }
 
 static ssize_t sdebug_delay_show(struct device_driver * ddp, char * buf) 
--- 1.12/drivers/scsi/scsi_debug.h	Sun Mar 16 10:05:12 2003
+++ edited/drivers/scsi/scsi_debug.h	Wed Apr 23 12:18:49 2003
@@ -14,7 +14,8 @@
 static int scsi_debug_bus_reset(struct scsi_cmnd *);
 static int scsi_debug_device_reset(struct scsi_cmnd *);
 static int scsi_debug_host_reset(struct scsi_cmnd *);
-static int scsi_debug_proc_info(char *, char **, off_t, int, int, int);
+static int scsi_debug_store_info(struct Scsi_Host *, const char *, size_t);
+static int scsi_debug_show_info(struct Scsi_Host *, struct seq_file *);
 static const char * scsi_debug_info(struct Scsi_Host *);
 
 /*
@@ -25,7 +26,8 @@
 #define SCSI_DEBUG_MAX_CMD_LEN 16
 
 static Scsi_Host_Template sdebug_driver_template = {
-	.proc_info =		scsi_debug_proc_info,
+	.show_info =		scsi_debug_show_info,
+	.store_info =		scsi_debug_store_info,
 	.name =			"SCSI DEBUG",
 	.info =			scsi_debug_info,
 	.slave_alloc =		scsi_debug_slave_alloc,
--- 1.18/drivers/scsi/scsi_proc.c	Sun Feb 23 13:34:56 2003
+++ edited/drivers/scsi/scsi_proc.c	Wed Apr 23 13:55:26 2003
@@ -23,6 +23,7 @@
 #include <linux/slab.h>
 #include <linux/proc_fs.h>
 #include <linux/errno.h>
+#include <linux/seq_file.h>
 #include <linux/blk.h>
 #include <asm/uaccess.h>
 
@@ -37,6 +39,58 @@
 EXPORT_SYMBOL(proc_scsi);
 
 
+static int proc_host_write(struct file *file, const char *buf,
+			   size_t count, loff_t *ppos)
+{
+	struct seq_file *m = file->private_data;
+	struct Scsi_Host *shost = m->private;
+	ssize_t res = -EFAULT;
+	char *page;
+
+	if (count > PROC_BLOCK_SIZE)
+		return -EOVERFLOW;
+
+	page = (char *)__get_free_page(GFP_KERNEL);
+	if (!page)
+		return -ENOMEM;
+
+	if (!copy_from_user(page, buf, count))
+		res = shost->hostt->store_info(shost, page, count);
+	free_page((unsigned long)page);
+	return res;
+}
+
+static int proc_host_show(struct seq_file *m, void *v)
+{
+	struct Scsi_Host *shost = m->private;
+	return shost->hostt->show_info(shost, m);
+}
+
+/*
+ * We need the seq_file even for O_WRONLY because proc_host_write
+ * dereferences it to get the host.  Storing the host directly in
+ * file->private_data would make O_RDWR impossible.
+ */
+static int proc_host_open(struct inode *inode, struct file *file)
+{
+	struct Scsi_Host *shost = PDE(inode)->data;
+
+	if ((file->f_mode & FMODE_READ) && !shost->hostt->show_info)
+		return -ENOSYS;
+	if ((file->f_mode & FMODE_WRITE) && !shost->hostt->store_info)
+		return -ENOSYS;
+
+	return single_open(file, proc_host_show, shost);
+}
+
+static struct file_operations proc_host_operations = {
+	.open		= proc_host_open,
+	.read           = seq_read,
+	.write		= proc_host_write,
+	.llseek         = seq_lseek,
+	.release        = single_release,
+};
+
 /* Used if the driver currently has no own support for /proc/scsi */
 static int generic_proc_info(char *buffer, char **start, off_t offset,
 			     int count, const char *(*info)(struct Scsi_Host *),
@@ -124,19 +178,24 @@
 		sht->proc_dir->owner = sht->module;
 	}
 
-	sprintf(name,"%d", shost->host_no);
-	p = create_proc_read_entry(name, S_IFREG | S_IRUGO | S_IWUSR,
-			shost->hostt->proc_dir, proc_scsi_read, shost);
-	if (!p) {
-		printk(KERN_ERR "%s: Failed to register host %d in"
-		       "%s\n", __FUNCTION__, shost->host_no,
-		       shost->hostt->proc_name);
-		return;
+	sprintf(name, "%d", shost->host_no);
+	p = create_proc_entry(name, S_IFREG | S_IRUGO | S_IWUSR,
+				shost->hostt->proc_dir);
+	if (p) {
+		p->owner = shost->hostt->module;
+		p->data = shost;
+
+		if (sht->show_info || sht->store_info) {
+			p->proc_fops = &proc_host_operations;
+		} else {
+			p->read_proc = proc_scsi_read;
+			p->write_proc = proc_scsi_write;
+		}
+	} else {
+		printk(KERN_ERR
+		    "Failed to register scsi%d with procfs.\n",
+		    shost->host_no);
 	} 
-
-	p->write_proc = proc_scsi_write;
-	p->owner = shost->hostt->module;
-
 }
 
 void scsi_proc_host_rm(struct Scsi_Host *shost)
--- 1.28/drivers/scsi/aic7xxx/aic7xxx_osm.c	Sun Apr 20 17:14:29 2003
+++ edited/drivers/scsi/aic7xxx/aic7xxx_osm.c	Wed Apr 23 12:29:37 2003
@@ -1268,7 +1268,8 @@
 Scsi_Host_Template aic7xxx_driver_template = {
 	.module			= THIS_MODULE,
 	.name			= "aic7xxx",
-	.proc_info		= ahc_linux_proc_info,
+	.show_info		= ahc_linux_show_info,
+	.store_info		= ahc_linux_store_info,
 	.info			= ahc_linux_info,
 	.queuecommand		= ahc_linux_queue,
 	.eh_abort_handler	= ahc_linux_abort,
@@ -4071,12 +4072,15 @@
 			printf("*): ");
 		else
 			printf("%d): ", target);
+#if 0	/* XXX(hch): need to come up with some fake seq_file stuff
+	   	     for this.  Should probably go into seq_file.c */
 		ahc_format_transinfo(&info, &tinfo->curr);
 		if (info.pos < info.length)
 			*info.buffer = '\0';
 		else
 			buf[info.length - 1] = '\0';
 		printf("%s", buf);
+#endif
 		break;
 	}
         case AC_SENT_BDR:
--- 1.35/drivers/scsi/aic7xxx/aic7xxx_osm.h	Sun Apr 20 17:14:29 2003
+++ edited/drivers/scsi/aic7xxx/aic7xxx_osm.h	Wed Apr 23 12:31:01 2003
@@ -68,6 +68,7 @@
 #include <linux/smp_lock.h>
 #include <linux/version.h>
 #include <linux/module.h>
+#include <linux/seq_file.h>
 #include <asm/byteorder.h>
 #include <asm/io.h>
 
@@ -673,7 +674,7 @@
 	int pos;
 };
 
-void	ahc_format_transinfo(struct info_str *info,
+void	ahc_format_transinfo(struct seq_file *s,
 			     struct ahc_transinfo *tinfo);
 
 /******************************** Locking *************************************/
@@ -1022,7 +1023,9 @@
 	(((dev_softc)->dma_mask = mask) && 0)
 #endif
 /**************************** Proc FS Support *********************************/
-int	ahc_linux_proc_info(char *, char **, off_t, int, int, int);
+int	ahc_linux_show_info(struct Scsi_Host *host, struct seq_file *s);
+int	ahc_linux_store_info(struct Scsi_Host *host,
+				       const char *buffer, size_t length);
 
 /*************************** Domain Validation ********************************/
 #define AHC_DV_CMD(cmd) ((cmd)->scsi_done == ahc_linux_dv_complete)
===== drivers/scsi/aic7xxx/aic7xxx_proc.c 1.7 vs edited =====
--- 1.7/drivers/scsi/aic7xxx/aic7xxx_proc.c	Thu Feb 20 17:50:09 2003
+++ edited/drivers/scsi/aic7xxx/aic7xxx_proc.c	Wed Apr 23 12:32:47 2003
@@ -43,61 +43,15 @@
 #include "aic7xxx_inline.h"
 #include "aic7xxx_93cx6.h"
 
-static void	copy_mem_info(struct info_str *info, char *data, int len);
-static int	copy_info(struct info_str *info, char *fmt, ...);
 static void	ahc_dump_target_state(struct ahc_softc *ahc,
-				      struct info_str *info,
+				      struct seq_file *s,
 				      u_int our_id, char channel,
 				      u_int target_id, u_int target_offset);
-static void	ahc_dump_device_state(struct info_str *info,
+static void	ahc_dump_device_state(struct seq_file *s,
 				      struct ahc_linux_device *dev);
-static int	ahc_proc_write_seeprom(struct ahc_softc *ahc,
-				       char *buffer, int length);
-
-static void
-copy_mem_info(struct info_str *info, char *data, int len)
-{
-	if (info->pos + len > info->offset + info->length)
-		len = info->offset + info->length - info->pos;
-
-	if (info->pos + len < info->offset) {
-		info->pos += len;
-		return;
-	}
-
-	if (info->pos < info->offset) {
-		off_t partial;
-
-		partial = info->offset - info->pos;
-		data += partial;
-		info->pos += partial;
-		len  -= partial;
-	}
-
-	if (len > 0) {
-		memcpy(info->buffer, data, len);
-		info->pos += len;
-		info->buffer += len;
-	}
-}
-
-static int
-copy_info(struct info_str *info, char *fmt, ...)
-{
-	va_list args;
-	char buf[256];
-	int len;
-
-	va_start(args, fmt);
-	len = vsprintf(buf, fmt, args);
-	va_end(args);
-
-	copy_mem_info(info, buf, len);
-	return (len);
-}
 
 void
-ahc_format_transinfo(struct info_str *info, struct ahc_transinfo *tinfo)
+ahc_format_transinfo(struct seq_file *s, struct ahc_transinfo *tinfo)
 {
 	u_int speed;
 	u_int freq;
@@ -112,12 +66,12 @@
 	speed *= (0x01 << tinfo->width);
         mb = speed / 1000;
         if (mb > 0)
-		copy_info(info, "%d.%03dMB/s transfers", mb, speed % 1000);
+		seq_printf(s, "%d.%03dMB/s transfers", mb, speed % 1000);
         else
-		copy_info(info, "%dKB/s transfers", speed);
+		seq_printf(s, "%dKB/s transfers", speed);
 
 	if (freq != 0) {
-		copy_info(info, " (%d.%03dMHz%s, offset %d",
+		seq_printf(s, " (%d.%03dMHz%s, offset %d",
 			 freq / 1000, freq % 1000,
 			 (tinfo->ppr_options & MSG_EXT_PPR_DT_REQ) != 0
 			 ? " DT" : "", tinfo->offset);
@@ -125,19 +79,19 @@
 
 	if (tinfo->width > 0) {
 		if (freq != 0) {
-			copy_info(info, ", ");
+			seq_printf(s, ", ");
 		} else {
-			copy_info(info, " (");
+			seq_printf(s, " (");
 		}
-		copy_info(info, "%dbit)", 8 * (0x01 << tinfo->width));
+		seq_printf(s, "%dbit)", 8 * (0x01 << tinfo->width));
 	} else if (freq != 0) {
-		copy_info(info, ")");
+		seq_printf(s, ")");
 	}
-	copy_info(info, "\n");
+	seq_printf(s, "\n");
 }
 
 static void
-ahc_dump_target_state(struct ahc_softc *ahc, struct info_str *info,
+ahc_dump_target_state(struct ahc_softc *ahc, struct seq_file *s,
 		      u_int our_id, char channel, u_int target_id,
 		      u_int target_offset)
 {
@@ -148,18 +102,18 @@
 
 	tinfo = ahc_fetch_transinfo(ahc, channel, our_id,
 				    target_id, &tstate);
-	copy_info(info, "Channel %c Target %d Negotiation Settings\n",
+	seq_printf(s, "Channel %c Target %d Negotiation Settings\n",
 		  channel, target_id);
-	copy_info(info, "\tUser: ");
-	ahc_format_transinfo(info, &tinfo->user);
+	seq_printf(s, "\tUser: ");
+	ahc_format_transinfo(s, &tinfo->user);
 	targ = ahc->platform_data->targets[target_offset];
 	if (targ == NULL)
 		return;
 
-	copy_info(info, "\tGoal: ");
-	ahc_format_transinfo(info, &tinfo->goal);
-	copy_info(info, "\tCurr: ");
-	ahc_format_transinfo(info, &tinfo->curr);
+	seq_printf(s, "\tGoal: ");
+	ahc_format_transinfo(s, &tinfo->goal);
+	seq_printf(s, "\tCurr: ");
+	ahc_format_transinfo(s, &tinfo->curr);
 
 	for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
 		struct ahc_linux_device *dev;
@@ -169,26 +123,28 @@
 		if (dev == NULL)
 			continue;
 
-		ahc_dump_device_state(info, dev);
+		ahc_dump_device_state(s, dev);
 	}
 }
 
 static void
-ahc_dump_device_state(struct info_str *info, struct ahc_linux_device *dev)
+ahc_dump_device_state(struct seq_file *s, struct ahc_linux_device *dev)
 {
-	copy_info(info, "\tChannel %c Target %d Lun %d Settings\n",
+	seq_printf(s, "\tChannel %c Target %d Lun %d Settings\n",
 		  dev->target->channel + 'A', dev->target->target, dev->lun);
 
-	copy_info(info, "\t\tCommands Queued %ld\n", dev->commands_issued);
-	copy_info(info, "\t\tCommands Active %d\n", dev->active);
-	copy_info(info, "\t\tCommand Openings %d\n", dev->openings);
-	copy_info(info, "\t\tMax Tagged Openings %d\n", dev->maxtags);
-	copy_info(info, "\t\tDevice Queue Frozen Count %d\n", dev->qfrozen);
+	seq_printf(s, "\t\tCommands Queued %ld\n", dev->commands_issued);
+	seq_printf(s, "\t\tCommands Active %d\n", dev->active);
+	seq_printf(s, "\t\tCommand Openings %d\n", dev->openings);
+	seq_printf(s, "\t\tMax Tagged Openings %d\n", dev->maxtags);
+	seq_printf(s, "\t\tDevice Queue Frozen Count %d\n", dev->qfrozen);
 }
 
-static int
-ahc_proc_write_seeprom(struct ahc_softc *ahc, char *buffer, int length)
+int
+ahc_linux_store_info(struct Scsi_Host *host,
+		const char *buffer, size_t length)
 {
+	struct ahc_softc *ahc = *(struct ahc_softc **)host->hostdata;
 	struct seeprom_descriptor sd;
 	int have_seeprom;
 	u_long s;
@@ -288,61 +244,33 @@
  * Return information to handle /proc support for the driver.
  */
 int
-ahc_linux_proc_info(char *buffer, char **start, off_t offset,
-		  int length, int hostno, int inout)
+ahc_linux_show_info(struct Scsi_Host *host, struct seq_file *s)
 {
-	struct	ahc_softc *ahc;
-	struct	info_str info;
+	struct ahc_softc *ahc = *(struct ahc_softc **)host->hostdata;
 	char	ahc_info[256];
-	u_long	s;
 	u_int	max_targ;
 	u_int	i;
-	int	retval;
-
-	retval = -EINVAL;
-	ahc_list_lock(&s);
-	TAILQ_FOREACH(ahc, &ahc_tailq, links) {
-		if (ahc->platform_data->host->host_no == hostno)
-			break;
-	}
-
-	if (ahc == NULL)
-		goto done;
 
-	 /* Has data been written to the file? */ 
-	if (inout == TRUE) {
-		retval = ahc_proc_write_seeprom(ahc, buffer, length);
-		goto done;
-	}
-
-	if (start)
-		*start = buffer;
-
-	info.buffer	= buffer;
-	info.length	= length;
-	info.offset	= offset;
-	info.pos	= 0;
-
-	copy_info(&info, "Adaptec AIC7xxx driver version: %s\n",
-		  AIC7XXX_DRIVER_VERSION);
-	copy_info(&info, "%s\n", ahc->description);
+	seq_printf(s, "Adaptec AIC7xxx driver version: %s\n",
+			AIC7XXX_DRIVER_VERSION);
+	seq_printf(s, "%s\n", ahc->description);
 	ahc_controller_info(ahc, ahc_info);
-	copy_info(&info, "%s\n\n", ahc_info);
+	seq_printf(s, "%s\n\n", ahc_info);
 
 	if (ahc->seep_config == NULL)
-		copy_info(&info, "No Serial EEPROM\n");
+		seq_printf(s, "No Serial EEPROM\n");
 	else {
-		copy_info(&info, "Serial EEPROM:\n");
+		seq_printf(s, "Serial EEPROM:\n");
 		for (i = 0; i < sizeof(*ahc->seep_config)/2; i++) {
 			if (((i % 8) == 0) && (i != 0)) {
-				copy_info(&info, "\n");
+				seq_printf(s, "\n");
 			}
-			copy_info(&info, "0x%.4x ",
+			seq_printf(s, "0x%.4x ",
 				  ((uint16_t*)ahc->seep_config)[i]);
 		}
-		copy_info(&info, "\n");
+		seq_printf(s, "\n");
 	}
-	copy_info(&info, "\n");
+	seq_printf(s, "\n");
 
 	max_targ = 15;
 	if ((ahc->features & (AHC_WIDE|AHC_TWIN)) == 0)
@@ -362,11 +290,8 @@
 			target_id = i % 8;
 		}
 
-		ahc_dump_target_state(ahc, &info, our_id,
-				      channel, target_id, i);
+		ahc_dump_target_state(ahc, s, our_id, channel, target_id, i);
 	}
-	retval = info.pos > info.offset ? info.pos - info.offset : 0;
-done:
-	ahc_list_unlock(&s);
-	return (retval);
+
+	return 0;
 }

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2003-04-25 15:04 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2003-04-23 19:21 [PATCH] proper replacements for ->proc_info Christoph Hellwig
2003-04-23 19:29 ` James Bottomley
2003-04-23 19:39   ` Christoph Hellwig
2003-04-25 10:27   ` Christoph Hellwig
2003-04-25 10:41     ` viro
2003-04-25 10:59       ` Christoph Hellwig
2003-04-25 14:52         ` Christoph Hellwig
2003-04-25 15:17           ` Christoph Hellwig

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox