[PATCH 4/4] 2.6.18-mm2 pktcdvd: sysfs + debugfs interface, bio write congestion handling

Thomas Maier
2006-10-01T18:54:42Z

[-- 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__ */