public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 4/4] 2.6.18-mm2 pktcdvd: sysfs + debugfs interface, bio write congestion handling
@ 2006-10-01 18:55 Thomas Maier
  2006-10-02 22:01 ` Peter Osterlund
  0 siblings, 1 reply; 2+ messages in thread
From: Thomas Maier @ 2006-10-01 18:55 UTC (permalink / raw)
  To: linux-kernel@vger.kernel.org; +Cc: petero2@telia.com, akpm

[-- Attachment #1: Type: text/plain, Size: 830 bytes --]

Hi,

this patch 4/4 for pktcdvd against Linux 2.6.18 (stable)
or 2.6.18-mm2 adds a sysfs and a debugfs interface. Also it adds
the ability to control the bio write queue of the driver (write
congestion control).
For more infos see the Documentation/* files in the patch.

You must first apply patch 1/4, 2/4 and 3/4 !

Sorry, my mail client damages the patch, so please use the (text) attachment
or get the patch from
http://people.freenet.de/BalaGi/download/4-sysfs-pktcdvd-patch-2.6.18

The merged patch (parts 1 to 4) is available at:
http://people.freenet.de/BalaGi/download/pktcdvd-patch-2.6.18

There is also a tool available to control the pktcdvd driver using
the new sysfs interface:
http://people.freenet.de/BalaGi/download/pktcdvd
http://people.freenet.de/BalaGi#pktcdvd

Signed-off-by: Thomas Maier<balagi@justmail.de>

[-- Attachment #2: 4-sysfs-pktcdvd-patch-2.6.18 --]
[-- Type: application/octet-stream, Size: 29250 bytes --]

diff -urpN p3-restruct/Documentation/ABI/testing/debugfs-pktcdvd p4-sysfs/Documentation/ABI/testing/debugfs-pktcdvd
--- p3-restruct/Documentation/ABI/testing/debugfs-pktcdvd	1970-01-01 01:00:00.000000000 +0100
+++ p4-sysfs/Documentation/ABI/testing/debugfs-pktcdvd	2006-10-01 18:27:19.000000000 +0200
@@ -0,0 +1,20 @@
+What:           /debug/pktcdvd[0-7]
+Date:           Sep. 2006
+KernelVersion:  2.6.19
+Contact:        Thomas Maier <balagi@justmail.de>
+Description:
+
+debugfs interface
+-----------------
+
+The pktcdvd module (packet writing driver) creates
+these files in debugfs:
+
+/debug/pktcdvd[0-7]/
+    info            (0444) Lots of human readable driver
+                           statistics and infos. Multiple lines!
+
+Example:
+-------
+
+cat /debug/pktcdvd3/info
diff -urpN p3-restruct/Documentation/ABI/testing/sysfs-class-pktcdvd p4-sysfs/Documentation/ABI/testing/sysfs-class-pktcdvd
--- p3-restruct/Documentation/ABI/testing/sysfs-class-pktcdvd	1970-01-01 01:00:00.000000000 +0100
+++ p4-sysfs/Documentation/ABI/testing/sysfs-class-pktcdvd	2006-10-01 18:27:25.000000000 +0200
@@ -0,0 +1,78 @@
+What:           /sys/class/pktcdvd/
+Date:           Sep. 2006
+KernelVersion:  2.6.19
+Contact:        Thomas Maier <balagi@justmail.de>
+Description:
+
+sysfs interface
+---------------
+
+The pktcdvd module (packet writing driver) creates
+these files in the sysfs:
+(<devid> is in format  major:minor )
+
+/sys/class/pktcdvd/
+    add            (0200)  Write a block device id to create a
+                           new pktcdvd device and map it to the
+                           block device.
+
+    remove         (0200)  Write the pktcdvd device id or the
+                           mapped block device id to it, to
+                           remove the pktcdvd device.
+
+    device_map     (0444)  Shows the device mapping in format:
+                             pktcdvd[0-7] <pktdevid> <blkdevid>
+
+    packet_buffers (0644)  Number of concurrent packets per
+                           pktcdvd device. Used for new created
+                           devices.
+	
+/sys/class/pktcdvd/pktcdvd[0-7]/
+    dev                   (0444) Device id
+    uevent                (0200) To send an uevent.
+    
+/sys/class/pktcdvd/pktcdvd[0-7]/stat/
+    packets_started       (0444) Number of started packets.
+    packets_finished      (0444) Number of finished packets.
+
+    kb_written            (0444) kBytes written.
+    kb_read               (0444) kBytes read.
+    kb_read_gather        (0444) kBytes read to fill write packets.
+    
+    reset                 (0200) Write any value to it to reset
+                                 pktcdvd device statistic values, like
+                                 bytes read/written.
+    
+/sys/class/pktcdvd/pktcdvd[0-7]/write_queue/
+    size                  (0444) Contains the size of the bio write
+                                 queue.
+
+    congestion_off        (0644) If bio write queue size is below
+                                 this mark, accept new bio requests
+                                 from the block layer.
+
+    congestion_on         (0644) If bio write queue size is higher
+                                 as this mark, do no longer accept
+                                 bio write requests from the block
+                                 layer and wait till the pktcdvd
+                                 device has processed enough bio's
+                                 so that bio write queue size is
+                                 below congestion off mark.
+
+ 
+
+Example:
+--------
+To use the pktcdvd sysfs interface directly, you can do:
+
+# create a new pktcdvd device mapped to /dev/hdc
+echo "22:0" >/sys/class/pktcdvd/add
+cat /sys/class/pktcdvd/device_map
+# assuming device pktcdvd0 was created, look at stat's
+cat /sys/class/pktcdvd/pktcdvd0/stat/kb_written
+# print the device id of the mapped block device
+fgrep pktcdvd0 /sys/class/pktcdvd/device_map
+# remove device, using pktcdvd0 device id   253:0
+echo "253:0" >/sys/class/pktcdvd/remove
+# same as using the mapped block device id  22:0
+echo "22:0" >/sys/class/pktcdvd/remove
diff -urpN p3-restruct/Documentation/cdrom/packet-writing.txt p4-sysfs/Documentation/cdrom/packet-writing.txt
--- p3-restruct/Documentation/cdrom/packet-writing.txt	2006-10-01 16:41:45.000000000 +0200
+++ p4-sysfs/Documentation/cdrom/packet-writing.txt	2006-10-01 18:26:26.000000000 +0200
@@ -1,13 +1,15 @@
 Getting started quick
 ---------------------
 
-- Select packet support in the block device section and UDF support in
-  the file system section.
+- Select packet support in the block device section (and enable
+  the pktcdvd procfs interface) and UDF support in the file
+  system section.
 
 - Compile and install kernel and modules, reboot.
 
 - You need the udftools package (pktsetup, mkudffs, cdrwtool).
   Download from http://sourceforge.net/projects/linux-udf/
+  Note: pktsetup needs the pktcdvd procfs interface!
 
 - Grab a new CD-RW disc and format it (assuming CD-RW is hdc, substitute
   as appropriate):
@@ -90,6 +92,59 @@ Notes
   to create an ext2 filesystem on the disc.
 
 
+
+Using the pktcdvd sysfs interface
+---------------------------------
+
+The pktcdvd module has a sysfs interface and can be controlled
+by the tool "pktcdvd" that uses sysfs.
+(see http://people.freenet.de/BalaGi#pktcdvd )
+
+"pktcdvd" works similar to "pktsetup", e.g.:
+
+	# pktcdvd -a dev_name /dev/hdc
+	# mkudffs /dev/pktcdvd/dev_name
+	# mount -t udf -o rw,noatime /dev/pktcdvd/dev_name /dvdram
+	# cp files /dvdram
+	# umount /dvdram
+	# pktcdvd -r dev_name
+
+
+For a description of the sysfs interface look into the file:
+
+  Documentation/ABI/testing/sysfs-block-pktcdvd
+
+
+Using the pktcdvd debugfs interface
+-----------------------------------
+
+To read pktcdvd device infos in human readable form, do:
+
+	# cat /debug/pktcdvd[0-7]/info
+
+For a description of the debugfs interface look into the file:
+
+  Documentation/ABI/testing/debugfs-pktcdvd
+
+
+
+Bio write queue congestion marks
+--------------------------------
+The pktcdvd driver allows now to adjust the behaviour of the
+internal bio write queue.
+This can be done with the two write_congestion_[on|off] marks.
+The driver does only accept up to write_congestion_on bio
+write request from the i/o block layer, and waits till the
+requests are processed by the mapped block device and
+the queue size is below the write_congestion_off mark.
+In previous versions of pktcdvd, the driver accepted all
+incoming bio write request. This led sometimes to kernel
+out of memory oops (maybe some bugs in the linux kernel ;)
+CAUTION: use this options only if you know what you do!
+The default settings for the congestion marks should be ok
+for everyone.
+	                 
+  
 Links
 -----
 
diff -urpN p3-restruct/drivers/block/Kconfig p4-sysfs/drivers/block/Kconfig
--- p3-restruct/drivers/block/Kconfig	2006-10-01 16:41:45.000000000 +0200
+++ p4-sysfs/drivers/block/Kconfig	2006-10-01 19:01:01.000000000 +0200
@@ -428,17 +428,22 @@ config CDROM_PKTCDVD
 	tristate "Packet writing on CD/DVD media"
 	depends on !UML
 	help
-	  If you have a CDROM drive that supports packet writing, say Y to
-	  include preliminary support. It should work with any MMC/Mt Fuji
-	  compliant ATAPI or SCSI drive, which is just about any newer CD
-	  writer.
+	  If you have a CDROM/DVD drive that supports packet writing, say
+	  Y to include preliminary support. It should work with any
+	  MMC/Mt Fuji compliant ATAPI or SCSI drive, which is just about
+	  any newer DVD/CD writer.
 
-	  Currently only writing to CD-RW, DVD-RW and DVD+RW discs is possible.
+	  Currently only writing to CD-RW, DVD-RW, DVD+RW and DVDRAM discs
+	  is possible.
 	  DVD-RW disks must be in restricted overwrite mode.
 
 	  To compile this driver as a module, choose M here: the
 	  module will be called pktcdvd.
 
+	  For further information see the file
+
+	    Documentation/cdrom/packet-writing.txt
+	  
 config CDROM_PKTCDVD_BUFFERS
 	int "Free buffers for data gathering"
 	depends on CDROM_PKTCDVD
diff -urpN p3-restruct/drivers/block/pktcdvd.c p4-sysfs/drivers/block/pktcdvd.c
--- p3-restruct/drivers/block/pktcdvd.c	2006-10-01 19:18:05.000000000 +0200
+++ p4-sysfs/drivers/block/pktcdvd.c	2006-10-01 19:19:27.000000000 +0200
@@ -58,6 +58,8 @@
 #include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_ioctl.h>
 #include <scsi/scsi.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
 
 #if PKT_USE_PROCFS
 #include <linux/proc_fs.h>
@@ -86,12 +88,17 @@
 
 /* module parameters */
 static int pktdev_major = 0; /* default: dynamic major number */
-
+static int write_congestion_on  = PKT_WRITE_CONGESTION_ON;
+static int write_congestion_off = PKT_WRITE_CONGESTION_OFF;
+static int packet_buffers = PKT_BUFFERS_DEFAULT;
 
 static struct pktcdvd_device *pkt_devs[MAX_WRITERS];
 static struct mutex ctl_mutex;	/* Serialize open/close/setup/teardown */
 static mempool_t *psd_pool;
 
+static struct class	*class_pktcdvd = NULL;
+static struct dentry	*pkt_debugfs_root = NULL;
+
 
 /* forward declaration */
 static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev);
@@ -141,6 +148,31 @@ static struct pktcdvd_device *pkt_find_d
 	return NULL;
 }
 
+static void init_congestion(int* lo, int* hi)
+{
+	if (*hi > 0) {
+		*hi = max(*hi, PKT_WRITE_CONGESTION_MIN);
+		*hi = min(*hi, PKT_WRITE_CONGESTION_MAX);
+		if (*lo <= 0)
+			*lo = *hi - PKT_WRITE_CONGESTION_THRESHOLD;
+		else {
+			*lo = min(*lo, *hi - PKT_WRITE_CONGESTION_THRESHOLD);
+			*lo = max(*lo, PKT_WRITE_CONGESTION_MIN/4);
+		}
+	} else {
+		*hi = -1;
+		*lo = -1;
+	}
+}
+
+static void init_packet_buffers(int *n)
+{
+	if (*n < 1)
+		*n = 1;
+	else if (*n > 128)
+		*n = 128;
+}
+
 static void pkt_count_states(struct pktcdvd_device *pd, int *states)
 {
 	struct packet_data *pkt;
@@ -189,6 +221,7 @@ static int pkt_print_info(struct pktcdvd
 	else
 		msg = "Unknown";
 	PRINT(BC, "\tblock mode:\t\t%s\n", msg);
+	PRINT(BC, "\tconcurrent packets:\t%d\n", packet_buffers);
 
 	PRINT(BC, "\nStatistics:\n");
 	PRINT(BC, "\tpackets started:\t%lu\n", pd->stats.pkt_started);
@@ -214,12 +247,415 @@ static int pkt_print_info(struct pktcdvd
 	pkt_count_states(pd, states);
 	PRINT(BC, "\tstate:\t\t\ti:%d ow:%d rw:%d ww:%d rec:%d fin:%d\n",
 	      states[0], states[1], states[2], states[3], states[4], states[5]);
+		   
+	PRINT(BC, "\twrite congestion marks:\toff=%d on=%d\n",
+			pd->write_congestion_off,
+			pd->write_congestion_on);
 #undef PRINT
 #undef BC
 	buf[blen-1] = 0;
 	return n;
 }
 
+static struct pktcdvd_dev_kobj* pkt_kobj_create(struct pktcdvd_device *pd,
+				const char* name,
+				struct kobject* parent,
+				struct kobj_type* ktype)
+{
+	struct pktcdvd_dev_kobj *p;
+	p = kzalloc(sizeof(*p), GFP_KERNEL);
+	if (!p)
+		return NULL;
+	kobject_set_name(&p->kobj, "%s", name);
+	p->kobj.parent = parent;
+	p->kobj.ktype = ktype;
+	p->pd = pd;
+	if (kobject_register(&p->kobj) != 0)
+		return NULL;
+	return p;
+}
+static void pkt_kobj_remove(struct pktcdvd_dev_kobj *p)
+{
+	if (p)
+		kobject_unregister(&p->kobj);
+}
+static void pkt_kobj_release(struct kobject *kobj)
+{
+	kfree(to_pktdevkobj(kobj));
+}
+/**********************************************************
+ *
+ * sysfs interface for pktcdvd
+ * by (C) 2006  Thomas Maier <balagi@justmail.de>
+ *
+ **********************************************************/
+
+#define DEF_ATTR(_obj,_name,_mode) \
+	static struct attribute _obj = { \
+		.name = _name, .owner = THIS_MODULE, .mode = _mode }
+
+/**********************************************************
+  /sys/class/pktcdvd/pktcdvd[0-7]/
+                     write_queue/size
+                     write_queue/congestion_off
+                     write_queue/congestion_on
+                     stat/reset
+                     stat/packets_started
+                     stat/packets_finished
+                     stat/kb_written
+                     stat/kb_read
+                     stat/kb_read_gather
+ **********************************************************/
+
+DEF_ATTR(kobj_pkt_attr_wq1, "size", 0444);
+DEF_ATTR(kobj_pkt_attr_wq2, "congestion_off", 0644);
+DEF_ATTR(kobj_pkt_attr_wq3, "congestion_on",  0644);
+
+static struct attribute *kobj_pkt_attrs_wqueue[] = {
+	&kobj_pkt_attr_wq1,
+	&kobj_pkt_attr_wq2,
+	&kobj_pkt_attr_wq3,
+	NULL
+};
+
+DEF_ATTR(kobj_pkt_attr_st1, "reset", 0200);
+DEF_ATTR(kobj_pkt_attr_st2, "packets_started", 0444);
+DEF_ATTR(kobj_pkt_attr_st3, "packets_finished", 0444);
+DEF_ATTR(kobj_pkt_attr_st4, "kb_written", 0444);
+DEF_ATTR(kobj_pkt_attr_st5, "kb_read", 0444);
+DEF_ATTR(kobj_pkt_attr_st6, "kb_read_gather", 0444);
+
+static struct attribute *kobj_pkt_attrs_stat[] = {
+	&kobj_pkt_attr_st1,
+	&kobj_pkt_attr_st2,
+	&kobj_pkt_attr_st3,
+	&kobj_pkt_attr_st4,
+	&kobj_pkt_attr_st5,
+	&kobj_pkt_attr_st6,
+	NULL
+};
+
+
+#define DECLARE_BUF_AS_STRING(_dbuf, _b, _l) \
+	char _dbuf[64]; int dlen = (_l) < 0 ? 0 : (_l); \
+	if (dlen >= sizeof(_dbuf)) dlen = sizeof(_dbuf)-1; \
+	memcpy(_dbuf, _b, dlen); _dbuf[dlen] = 0
+	
+
+static ssize_t kobj_pkt_show(struct kobject *kobj,
+			struct attribute *attr, char *data)
+{
+	struct pktcdvd_device *pd;
+	int n = 0;
+	int v;
+
+	data[0] = 0;
+	pd = to_pktdevkobj(kobj)->pd;
+
+	if (strcmp(attr->name, "packets_started") == 0) {
+		n = sprintf(data, "%lu\n", pd->stats.pkt_started);
+
+	} else if (strcmp(attr->name, "packets_finished") == 0) {
+		n = sprintf(data, "%lu\n", pd->stats.pkt_ended);
+
+	} else if (strcmp(attr->name, "kb_written") == 0) {
+		n = sprintf(data, "%lu\n", pd->stats.secs_w >> 1);
+
+	} else if (strcmp(attr->name, "kb_read") == 0) {
+		n = sprintf(data, "%lu\n", pd->stats.secs_r >> 1);
+
+	} else if (strcmp(attr->name, "kb_read_gather") == 0) {
+		n = sprintf(data, "%lu\n", pd->stats.secs_rg >> 1);
+	
+	} else if (strcmp(attr->name, "size") == 0) {
+		spin_lock(&pd->lock);
+		v = pd->bio_queue_size;
+		spin_unlock(&pd->lock);
+		n = sprintf(data, "%d\n", v);
+		
+	} else if (strcmp(attr->name, "congestion_off") == 0) {
+		spin_lock(&pd->lock);
+		v = pd->write_congestion_off;
+		spin_unlock(&pd->lock);
+		n = sprintf(data, "%d\n", v);
+
+	} else if (strcmp(attr->name, "congestion_on") == 0) {
+		spin_lock(&pd->lock);
+		v = pd->write_congestion_on;
+		spin_unlock(&pd->lock);
+		n = sprintf(data, "%d\n", v);
+	}
+	return n;
+}
+
+static ssize_t kobj_pkt_store(struct kobject *kobj,
+			struct attribute *attr,
+			const char *data, size_t len)
+{
+	int val;
+	struct pktcdvd_device *pd;
+	DECLARE_BUF_AS_STRING(dbuf, data, len);
+
+	pd = to_pktdevkobj(kobj)->pd;
+
+	if (strcmp(attr->name, "reset") == 0 && dlen > 0) {
+		pd->stats.pkt_started = 0;
+		pd->stats.pkt_ended = 0;
+		pd->stats.secs_w = 0;
+		pd->stats.secs_rg = 0;
+		pd->stats.secs_r = 0;
+		
+	} else if (strcmp(attr->name, "congestion_off") == 0
+		   && sscanf(dbuf, "%d", &val) == 1) {
+		spin_lock(&pd->lock);
+		pd->write_congestion_off = val;
+		init_congestion(&pd->write_congestion_off,
+				&pd->write_congestion_on);
+		spin_unlock(&pd->lock);
+	} else if (strcmp(attr->name, "congestion_on") == 0
+		   && sscanf(dbuf, "%d", &val) == 1) {
+		spin_lock(&pd->lock);
+		pd->write_congestion_on = val;
+		init_congestion(&pd->write_congestion_off,
+				&pd->write_congestion_on);
+		spin_unlock(&pd->lock);
+	}
+
+	return len;
+}
+
+static struct sysfs_ops kobj_pkt_ops = {
+	.show = kobj_pkt_show,
+	.store = kobj_pkt_store
+};
+static struct kobj_type kobj_pkt_type_stat = {
+	.release = pkt_kobj_release,
+	.sysfs_ops = &kobj_pkt_ops,
+	.default_attrs = kobj_pkt_attrs_stat
+};
+static struct kobj_type kobj_pkt_type_wqueue = {
+	.release = pkt_kobj_release,
+	.sysfs_ops = &kobj_pkt_ops,
+	.default_attrs = kobj_pkt_attrs_wqueue
+};
+
+
+static void pkt_sysfs_dev_new(struct pktcdvd_device *pd)
+{
+	if (class_pktcdvd) {
+		pd->clsdev = class_device_create(class_pktcdvd,
+					NULL, pd->pkt_dev,
+					NULL, "%s", pd->name);
+		if (IS_ERR(pd->clsdev))
+			pd->clsdev = NULL;
+	}
+	if (pd->clsdev) {
+		pd->kobj_stat = pkt_kobj_create(pd, "stat",
+					&pd->clsdev->kobj,
+					&kobj_pkt_type_stat);
+		pd->kobj_wqueue = pkt_kobj_create(pd, "write_queue",
+					&pd->clsdev->kobj,
+					&kobj_pkt_type_wqueue);
+	}
+}
+
+static void pkt_sysfs_dev_remove(struct pktcdvd_device *pd)
+{
+	pkt_kobj_remove(pd->kobj_stat);
+	pkt_kobj_remove(pd->kobj_wqueue);
+	if (class_pktcdvd)
+		class_device_destroy(class_pktcdvd, pd->pkt_dev);
+}
+
+
+/********************************************************************
+  /sys/class/pktcdvd/
+                     add            map block device
+                     remove         unmap packet dev
+                     device_map     show mappings
+                     packet_buffers number of packet buffers per dev
+ *******************************************************************/
+
+static void class_pktcdvd_release(struct class *cls)
+{
+	kfree(cls);
+}
+static ssize_t class_pktcdvd_show_map(struct class *c, char *data)
+{
+	int n = 0;
+	int idx;
+	data[0] = 0;
+	mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
+	for (idx = 0; idx < MAX_WRITERS; idx++) {
+		struct pktcdvd_device *pd = pkt_devs[idx];
+		if (!pd)
+			continue;
+		n += sprintf(data+n, "%s %u:%u %u:%u\n",
+			pd->name,
+			MAJOR(pd->pkt_dev), MINOR(pd->pkt_dev),
+			MAJOR(pd->bdev->bd_dev),
+			MINOR(pd->bdev->bd_dev));
+	}
+	mutex_unlock(&ctl_mutex);
+	return n;
+}
+static ssize_t class_pktcdvd_show_pb(struct class *c, char *data)
+{
+	return sprintf(data, "%d\n", packet_buffers);
+}
+	
+static ssize_t class_pktcdvd_store_add(struct class *c, const char *buf,
+					size_t count)
+{
+	unsigned int major, minor;
+	DECLARE_BUF_AS_STRING(dbuf, buf, count);
+	if (sscanf(dbuf, "%u:%u", &major, &minor) == 2) {
+		pkt_setup_dev(MKDEV(major, minor), NULL);
+		return count;
+	}
+	return -EINVAL;
+}
+static ssize_t class_pktcdvd_store_remove(struct class *c, const char *buf,
+					size_t count)
+{
+	unsigned int major, minor;
+	DECLARE_BUF_AS_STRING(dbuf, buf, count);
+	if (sscanf(dbuf, "%u:%u", &major, &minor) == 2) {
+		pkt_remove_dev(MKDEV(major, minor));
+		return count;
+	}
+	return -EINVAL;
+}
+static ssize_t class_pktcdvd_store_pb(struct class *c, const char *buf,
+					size_t count)
+{
+	int val;
+	DECLARE_BUF_AS_STRING(dbuf, buf, count);
+	if (sscanf(dbuf, "%d", &val) == 1) {
+		mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
+		init_packet_buffers(&val);
+		packet_buffers = val;
+		mutex_unlock(&ctl_mutex);
+		return count;
+	}
+	return -EINVAL;
+}
+
+static struct class_attribute class_pktcdvd_attrs[] = {
+ __ATTR(add,            0200, NULL, class_pktcdvd_store_add),
+ __ATTR(remove,         0200, NULL, class_pktcdvd_store_remove),
+ __ATTR(device_map,     0444, class_pktcdvd_show_map, NULL),
+ __ATTR(packet_buffers, 0644, class_pktcdvd_show_pb, class_pktcdvd_store_pb),
+ __ATTR_NULL
+};
+
+
+static int pkt_sysfs_init(void)
+{
+	int ret = 0;
+	
+	/*
+	 * create control files in sysfs
+	 * /sys/class/pktcdvd/...
+	 */
+	class_pktcdvd = kzalloc(sizeof(*class_pktcdvd), GFP_KERNEL);
+	if (!class_pktcdvd)
+		return -ENOMEM;
+	class_pktcdvd->name = DRIVER_NAME;
+	class_pktcdvd->owner = THIS_MODULE;
+	class_pktcdvd->class_release = class_pktcdvd_release;
+	class_pktcdvd->class_attrs = class_pktcdvd_attrs;
+	ret = class_register(class_pktcdvd);
+	if (ret) {
+		kfree(class_pktcdvd);
+		class_pktcdvd = NULL;
+		printk(DRIVER_NAME": failed to create class pktcdvd\n");
+		return ret;
+	}
+	return 0;
+}
+
+static void pkt_sysfs_cleanup(void)
+{
+	if (class_pktcdvd)
+		class_destroy(class_pktcdvd);
+	class_pktcdvd = NULL;
+}
+
+/********************************************************************
+  entries in debugfs
+
+  /debugfs/pktcdvd[0-7]/
+			info
+  
+ *******************************************************************/
+static int pkt_debugfs_seq_show(struct seq_file *m, void *p)
+{
+	struct pktcdvd_device *pd = m->private;
+	char buf[1024];
+	
+	pkt_print_info(pd, buf, sizeof(buf));
+	seq_printf(m, "%s", buf);
+	return 0;
+}
+
+static int pkt_debugfs_fops_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, pkt_debugfs_seq_show, inode->u.generic_ip);
+}
+
+static struct file_operations debug_fops = {
+	.open		= pkt_debugfs_fops_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+	.owner		= THIS_MODULE,
+};
+ 
+static void pkt_debugfs_dev_new(struct pktcdvd_device *pd)
+{
+	if (!pkt_debugfs_root)
+		return;
+	pd->dfs_d_root = debugfs_create_dir(pd->name, pkt_debugfs_root);
+	if (IS_ERR(pd->dfs_d_root)) {
+		pd->dfs_d_root = NULL;
+		return;
+	}
+	pd->dfs_f_info = debugfs_create_file("info", S_IRUGO,
+				pd->dfs_d_root, pd, &debug_fops);
+	if (IS_ERR(pd->dfs_f_info)) {
+		pd->dfs_f_info = NULL;
+		return;
+	}
+}
+static void pkt_debugfs_dev_remove(struct pktcdvd_device *pd)
+{
+	if (!pkt_debugfs_root)
+		return;
+	if (pd->dfs_f_info)
+		debugfs_remove(pd->dfs_f_info);
+	pd->dfs_f_info = NULL;
+	if (pd->dfs_d_root)
+		debugfs_remove(pd->dfs_d_root);
+	pd->dfs_d_root = NULL;
+}
+static void pkt_debugfs_init(void)
+{
+	pkt_debugfs_root = debugfs_create_dir(DRIVER_NAME, NULL);
+	if (IS_ERR(pkt_debugfs_root)) {
+		pkt_debugfs_root = NULL;
+		return;
+	}
+}
+static void pkt_debugfs_cleanup(void)
+{
+	if (!pkt_debugfs_root)
+		return;
+	debugfs_remove(pkt_debugfs_root);
+	pkt_debugfs_root = NULL;
+}
+
+
 /********************************************************
  *
  * (old) procfs interface
@@ -1133,6 +1569,7 @@ static int pkt_handle_queue(struct pktcd
 	sector_t zone = 0; /* Suppress gcc warning */
 	struct pkt_rb_node *node, *first_node;
 	struct rb_node *n;
+	int wakeup;
 
 	VPRINTK("handle_queue\n");
 
@@ -1205,7 +1642,14 @@ try_next_bio:
 		pkt->write_size += bio->bi_size / CD_FRAMESIZE;
 		spin_unlock(&pkt->lock);
 	}
+	/* check write congestion marks, and if bio_queue_size is
+	   below, wake up any waiters */
+	wakeup = (pd->write_congestion_on > 0
+	 		&& pd->bio_queue_size <= pd->write_congestion_off
+			&& waitqueue_active(&pd->write_congestion_wqueue));
 	spin_unlock(&pd->lock);
+	if (wakeup)
+		wake_up(&pd->write_congestion_wqueue);
 
 	pkt->sleep_time = max(PACKET_WAIT_TIME, 1);
 	pkt_set_state(pkt, PACKET_WAITING_STATE);
@@ -2250,6 +2694,32 @@ static int pkt_make_request(request_queu
 	spin_unlock(&pd->cdrw.active_list_lock);
 
 	/*
+	 * Test if there is enough room left in the bio work queue
+	 * (queue size >= congestion on mark).
+	 * If not, wait till the work queue size is below the congestion off mark.
+	 * This is similar to the get_request_wait() call made in the block
+	 * layer function __make_request() used for normal block i/o request
+	 * handling.
+	 */
+	spin_lock(&pd->lock);
+	if (pd->write_congestion_on > 0
+	     && pd->bio_queue_size >= pd->write_congestion_on) {
+		DEFINE_WAIT(wait);
+		/* wait till number of bio requests is low enough */
+		do {
+			spin_unlock(&pd->lock);
+			prepare_to_wait_exclusive(&pd->write_congestion_wqueue,
+					&wait, TASK_UNINTERRUPTIBLE);
+			io_schedule();
+			/* if we are here, bio_queue_size should be below
+			   congestion_off, but be sure and do a test */
+			spin_lock(&pd->lock);
+		} while(pd->bio_queue_size > pd->write_congestion_off);
+		finish_wait(&pd->write_congestion_wqueue, &wait);
+	}
+	spin_unlock(&pd->lock);
+
+	/*
 	 * No matching packet found. Store the bio in the work queue.
 	 */
 	node = mempool_alloc(pd->rb_pool, GFP_NOIO);
@@ -2351,7 +2821,7 @@ static int pkt_open_dev(struct pktcdvd_d
 		goto out_unclaim;
 
 	if (write) {
-		if (!pkt_grow_pktlist(pd, CONFIG_CDROM_PKTCDVD_BUFFERS)) {
+		if (!pkt_grow_pktlist(pd, packet_buffers)) {
 			printk(DRIVER_NAME": not enough memory for buffers\n");
 			ret = -ENOMEM;
 			goto out_unclaim;
@@ -2570,6 +3040,15 @@ static int pkt_new_dev(struct pktcdvd_de
 	/* This is safe, since we have a reference from open(). */
 	__module_get(THIS_MODULE);
 
+	/* the block device must have a queue ! else many of the
+	   pktcdvd code breaks. */
+	if (!bdev_get_queue(bdev)) {
+		printk(DRIVER_NAME": block device %s has no request queue, aborting\n",
+			bdevname(bdev, b));
+		ret = -ENXIO;
+		goto out_mem;
+	}
+
 	pd->bdev = bdev;
 	set_blocksize(bdev, CD_FRAMESIZE);
 
@@ -2643,6 +3122,10 @@ static int pkt_setup_dev(dev_t dev, dev_
 	init_waitqueue_head(&pd->wqueue);
 	pd->bio_queue = RB_ROOT;
 
+	init_waitqueue_head(&pd->write_congestion_wqueue);
+	pd->write_congestion_on  = write_congestion_on;
+	pd->write_congestion_off = write_congestion_off;
+
 	disk = alloc_disk(1);
 	if (!disk)
 		goto out_mem;
@@ -2664,10 +3147,14 @@ static int pkt_setup_dev(dev_t dev, dev_
 
 	add_disk(disk);
 	
+	pkt_sysfs_dev_new(pd);
+
 	pkt_devs[idx] = pd;
 	if (pkt_dev)
 		*pkt_dev = pd->pkt_dev;
 
+	pkt_debugfs_dev_new(pd);
+	
 	mutex_unlock(&ctl_mutex);
 	return 0;
 
@@ -2716,6 +3203,10 @@ static int pkt_remove_dev(dev_t pkt_dev)
 	
 	pkt_devs[idx] = NULL;
 	
+	pkt_debugfs_dev_remove(pd);
+
+	pkt_sysfs_dev_remove(pd);
+	
 	blkdev_put(pd->bdev);
 
 #if PKT_USE_PROCFS
@@ -2748,6 +3239,10 @@ static int __init pkt_init(void)
 {
 	int ret;
 
+	/* check module parameters */
+	init_congestion(&write_congestion_off, &write_congestion_on);
+	init_packet_buffers(&packet_buffers);
+
 	mutex_init(&ctl_mutex);
 
 	psd_pool = mempool_create_kmalloc_pool(PSD_POOL_SIZE,
@@ -2763,17 +3258,26 @@ static int __init pkt_init(void)
 	if (!pktdev_major)
 		pktdev_major = ret;
 
+	ret = pkt_sysfs_init();
+	if (ret)
+		goto out;
+
 #if PKT_USE_PROCFS
 	ret = misc_register(&pkt_misc);
 	if (ret) {
 		printk(DRIVER_NAME": Unable to register misc device\n");
-		goto out;
+		goto out_misc;
 	}
 	pkt_proc = proc_mkdir(DRIVER_NAME, proc_root_driver);
 #endif
-
+	pkt_debugfs_init();
 	return 0;
 
+#if PKT_USE_PROCFS
+out_misc:
+	class_destroy(class_pktcdvd);
+	class_pktcdvd = NULL;
+#endif
 out:
 	unregister_blkdev(pktdev_major, DRIVER_NAME);
 out2:
@@ -2783,6 +3287,9 @@ out2:
 
 static void __exit pkt_exit(void)
 {
+	pkt_debugfs_cleanup();
+	pkt_sysfs_cleanup();
+	
 #if PKT_USE_PROCFS
 	remove_proc_entry(DRIVER_NAME, proc_root_driver);
 	misc_deregister(&pkt_misc);
@@ -2791,6 +3298,10 @@ static void __exit pkt_exit(void)
 	mempool_destroy(psd_pool);
 }
 
+/***************************************************************
+ * module declaration
+ **************************************************************/
+
 MODULE_DESCRIPTION("Packet writing layer for CD/DVD drives");
 MODULE_AUTHOR("Jens Axboe <axboe@suse.de>,Peter Osterlund <petero2@telia.com>,"
               "Thomas Maier <balagi@justmail.de>");
@@ -2800,3 +3311,6 @@ module_init(pkt_init);
 module_exit(pkt_exit);
 
 module_param(pktdev_major, int, 0);
+module_param(write_congestion_on, int, 0);
+module_param(write_congestion_off, int, 0);
+module_param(packet_buffers, int, 0);
diff -urpN p3-restruct/include/linux/pktcdvd.h p4-sysfs/include/linux/pktcdvd.h
--- p3-restruct/include/linux/pktcdvd.h	2006-10-01 17:45:52.000000000 +0200
+++ p4-sysfs/include/linux/pktcdvd.h	2006-10-01 17:58:46.000000000 +0200
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2000 Jens Axboe <axboe@suse.de>
  * Copyright (C) 2001-2004 Peter Osterlund <petero2@telia.com>
+ * Copyright (C) 2006 Thomas Maier <balagi@justmail.de>
  *
  * May be copied or modified under the terms of the GNU General Public
  * License.  See linux/COPYING for more information.
@@ -39,10 +40,12 @@ struct pkt_ctrl_command {
  * defines and structs used in the kernel only
  *************************************************************************/
 #ifdef __KERNEL__
+#include <linux/config.h>
 #include <linux/blkdev.h>
 #include <linux/completion.h>
 #include <linux/cdrom.h>
-
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
 
 /*
  * 1 for normal debug messages, 2 is very verbose. 0 to turn it off.
@@ -76,6 +79,20 @@ struct pkt_ctrl_command {
 #define PKT_USE_PROCFS  0
 #endif
 
+/* use concurrent packets per device */
+#ifdef CONFIG_CDROM_PKTCDVD_BUFFERS
+#define PKT_BUFFERS_DEFAULT CONFIG_CDROM_PKTCDVD_BUFFERS
+#else
+#define PKT_BUFFERS_DEFAULT 8
+#endif
+
+/* default bio write queue congestion marks */
+#define PKT_WRITE_CONGESTION_ON 5000
+#define PKT_WRITE_CONGESTION_OFF 4500
+#define PKT_WRITE_CONGESTION_MAX (1024*1024)
+#define PKT_WRITE_CONGESTION_MIN 100
+#define PKT_WRITE_CONGESTION_THRESHOLD 25
+
 
 /*
  * device types
@@ -251,6 +268,15 @@ struct packet_stacked_data
 };
 #define PSD_POOL_SIZE		64
 
+
+struct pktcdvd_dev_kobj
+{
+	struct kobject		kobj;
+	struct pktcdvd_device	*pd;
+};
+#define to_pktdevkobj(_k) \
+  ((struct pktcdvd_dev_kobj*)container_of(_k,struct pktcdvd_dev_kobj,kobj))
+
 struct pktcdvd_device
 {
 	struct block_device	*bdev;		/* dev attached */
@@ -281,6 +307,17 @@ struct pktcdvd_device
 
 	struct packet_iosched   iosched;
 	struct gendisk		*disk;
+	
+	wait_queue_head_t	write_congestion_wqueue;
+	int			write_congestion_off;
+	int			write_congestion_on;
+
+	struct class_device	*clsdev;	/* sysfs pktcdvd[0-7] class dev */ 
+	struct pktcdvd_dev_kobj	*kobj_stat;	/* sysfs pktcdvd[0-7]/stat/     */
+	struct pktcdvd_dev_kobj	*kobj_wqueue;	/* sysfs pktcdvd[0-7]/write_queue/ */
+	
+	struct dentry		*dfs_d_root;	/* debugfs: devname directory */
+	struct dentry		*dfs_f_info;	/* debugfs: info file */
 };
 
 #endif /* __KERNEL__ */

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

* Re: [PATCH 4/4] 2.6.18-mm2 pktcdvd: sysfs + debugfs interface, bio write congestion handling
  2006-10-01 18:55 [PATCH 4/4] 2.6.18-mm2 pktcdvd: sysfs + debugfs interface, bio write congestion handling Thomas Maier
@ 2006-10-02 22:01 ` Peter Osterlund
  0 siblings, 0 replies; 2+ messages in thread
From: Peter Osterlund @ 2006-10-02 22:01 UTC (permalink / raw)
  To: balagi; +Cc: linux-kernel@vger.kernel.org, akpm

"Thomas Maier" <balagi@justmail.de> writes:

> this patch 4/4 for pktcdvd against Linux 2.6.18 (stable)
> or 2.6.18-mm2 adds a sysfs and a debugfs interface. Also it adds
> the ability to control the bio write queue of the driver (write
> congestion control).
> For more infos see the Documentation/* files in the patch.

The sysfs interface makes sense even without the congestion control,
so the two features should be two separate patches.

-- 
Peter Osterlund - petero2@telia.com
http://web.telia.com/~u89404340

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

end of thread, other threads:[~2006-10-02 22:02 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-10-01 18:55 [PATCH 4/4] 2.6.18-mm2 pktcdvd: sysfs + debugfs interface, bio write congestion handling Thomas Maier
2006-10-02 22:01 ` Peter Osterlund

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