summaryrefslogtreecommitdiff
path: root/drivers/block/pktcdvd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/block/pktcdvd.c')
-rw-r--r--drivers/block/pktcdvd.c689
1 files changed, 588 insertions, 101 deletions
diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c
index 451b996..7c95c76 100644
--- a/drivers/block/pktcdvd.c
+++ b/drivers/block/pktcdvd.c
@@ -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.
@@ -54,14 +55,18 @@
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/miscdevice.h>
-#include <linux/suspend.h>
+#include <linux/freezer.h>
#include <linux/mutex.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_ioctl.h>
#include <scsi/scsi.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
#include <asm/uaccess.h>
+#define DRIVER_NAME "pktcdvd"
+
#if PACKET_DEBUG
#define DPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args)
#else
@@ -80,16 +85,431 @@
static struct pktcdvd_device *pkt_devs[MAX_WRITERS];
static struct proc_dir_entry *pkt_proc;
-static int pkt_major;
+static int pktdev_major;
+static int write_congestion_on = PKT_WRITE_CONGESTION_ON;
+static int write_congestion_off = PKT_WRITE_CONGESTION_OFF;
static struct mutex ctl_mutex; /* Serialize open/close/setup/teardown */
static mempool_t *psd_pool;
+static struct class *class_pktcdvd = NULL; /* /sys/class/pktcdvd */
+static struct dentry *pkt_debugfs_root = NULL; /* /debug/pktcdvd */
+
+/* forward declaration */
+static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev);
+static int pkt_remove_dev(dev_t pkt_dev);
+static int pkt_seq_show(struct seq_file *m, void *p);
+
+
+
+/*
+ * create and register a pktcdvd kernel object.
+ */
+static struct pktcdvd_kobj* pkt_kobj_create(struct pktcdvd_device *pd,
+ const char* name,
+ struct kobject* parent,
+ struct kobj_type* ktype)
+{
+ struct pktcdvd_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;
+}
+/*
+ * remove a pktcdvd kernel object.
+ */
+static void pkt_kobj_remove(struct pktcdvd_kobj *p)
+{
+ if (p)
+ kobject_unregister(&p->kobj);
+}
+/*
+ * default release function for pktcdvd kernel objects.
+ */
+static void pkt_kobj_release(struct kobject *kobj)
+{
+ kfree(to_pktcdvdkobj(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]/
+ stat/reset
+ stat/packets_started
+ stat/packets_finished
+ stat/kb_written
+ stat/kb_read
+ stat/kb_read_gather
+ write_queue/size
+ write_queue/congestion_off
+ write_queue/congestion_on
+ **********************************************************/
+
+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
+};
+
+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
+};
+
+/* declares a char buffer[64] _dbuf, copies data from
+ * _b with length _l into it and ensures that _dbuf ends
+ * with a \0 character.
+ */
+#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 = to_pktcdvdkobj(kobj)->pd;
+ int n = 0;
+ int v;
+ 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 void init_write_congestion_marks(int* lo, int* hi)
+{
+ if (*hi > 0) {
+ *hi = max(*hi, 500);
+ *hi = min(*hi, 1000000);
+ if (*lo <= 0)
+ *lo = *hi - 100;
+ else {
+ *lo = min(*lo, *hi - 100);
+ *lo = max(*lo, 100);
+ }
+ } else {
+ *hi = -1;
+ *lo = -1;
+ }
+}
+
+static ssize_t kobj_pkt_store(struct kobject *kobj,
+ struct attribute *attr,
+ const char *data, size_t len)
+{
+ struct pktcdvd_device *pd = to_pktcdvdkobj(kobj)->pd;
+ int val;
+ DECLARE_BUF_AS_STRING(dbuf, data, len); /* ensure sscanf scans a string */
+
+ 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_write_congestion_marks(&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_write_congestion_marks(&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
+ *******************************************************************/
+
+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;
+ 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_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 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_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)
+{
+ return pkt_seq_show(m, p);
+}
+
+static int pkt_debugfs_fops_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pkt_debugfs_seq_show, inode->i_private);
+}
+
+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_f_info = NULL;
+ 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;
+}
+
+/* ----------------------------------------------------------*/
+
static void pkt_bio_finished(struct pktcdvd_device *pd)
{
BUG_ON(atomic_read(&pd->cdrw.pending_bios) <= 0);
if (atomic_dec_and_test(&pd->cdrw.pending_bios)) {
- VPRINTK("pktcdvd: queue empty\n");
+ VPRINTK(DRIVER_NAME": queue empty\n");
atomic_set(&pd->iosched.attention, 1);
wake_up(&pd->wqueue);
}
@@ -348,7 +768,7 @@ static int pkt_generic_packet(struct pktcdvd_device *pd, struct packet_command *
char sense[SCSI_SENSE_BUFFERSIZE];
request_queue_t *q;
struct request *rq;
- DECLARE_COMPLETION(wait);
+ DECLARE_COMPLETION_ONSTACK(wait);
int err = 0;
q = bdev_get_queue(pd->bdev);
@@ -365,17 +785,17 @@ static int pkt_generic_packet(struct pktcdvd_device *pd, struct packet_command *
rq->sense = sense;
memset(sense, 0, sizeof(sense));
rq->sense_len = 0;
- rq->flags |= REQ_BLOCK_PC | REQ_HARDBARRIER;
+ rq->cmd_type = REQ_TYPE_BLOCK_PC;
+ rq->cmd_flags |= REQ_HARDBARRIER;
if (cgc->quiet)
- rq->flags |= REQ_QUIET;
+ rq->cmd_flags |= REQ_QUIET;
memcpy(rq->cmd, cgc->cmd, CDROM_PACKET_SIZE);
if (sizeof(rq->cmd) > CDROM_PACKET_SIZE)
memset(rq->cmd + CDROM_PACKET_SIZE, 0, sizeof(rq->cmd) - CDROM_PACKET_SIZE);
rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
rq->ref_count++;
- rq->flags |= REQ_NOMERGE;
- rq->waiting = &wait;
+ rq->end_io_data = &wait;
rq->end_io = blk_end_sync_rq;
elv_add_request(q, rq, ELEVATOR_INSERT_BACK, 1);
generic_unplug_device(q);
@@ -400,7 +820,7 @@ static void pkt_dump_sense(struct packet_command *cgc)
int i;
struct request_sense *sense = cgc->sense;
- printk("pktcdvd:");
+ printk(DRIVER_NAME":");
for (i = 0; i < CDROM_PACKET_SIZE; i++)
printk(" %02x", cgc->cmd[i]);
printk(" - ");
@@ -528,7 +948,7 @@ static void pkt_iosched_process_queue(struct pktcdvd_device *pd)
need_write_seek = 0;
if (need_write_seek && reads_queued) {
if (atomic_read(&pd->cdrw.pending_bios) > 0) {
- VPRINTK("pktcdvd: write, waiting\n");
+ VPRINTK(DRIVER_NAME": write, waiting\n");
break;
}
pkt_flush_cache(pd);
@@ -537,7 +957,7 @@ static void pkt_iosched_process_queue(struct pktcdvd_device *pd)
} else {
if (!reads_queued && writes_queued) {
if (atomic_read(&pd->cdrw.pending_bios) > 0) {
- VPRINTK("pktcdvd: read, waiting\n");
+ VPRINTK(DRIVER_NAME": read, waiting\n");
break;
}
pd->iosched.writing = 1;
@@ -600,7 +1020,7 @@ static int pkt_set_segment_merging(struct pktcdvd_device *pd, request_queue_t *q
set_bit(PACKET_MERGE_SEGS, &pd->flags);
return 0;
} else {
- printk("pktcdvd: cdrom max_phys_segments too small\n");
+ printk(DRIVER_NAME": cdrom max_phys_segments too small\n");
return -EIO;
}
}
@@ -891,6 +1311,7 @@ static int pkt_handle_queue(struct pktcdvd_device *pd)
sector_t zone = 0; /* Suppress gcc warning */
struct pkt_rb_node *node, *first_node;
struct rb_node *n;
+ int wakeup;
VPRINTK("handle_queue\n");
@@ -963,7 +1384,13 @@ 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);
spin_unlock(&pd->lock);
+ if (wakeup)
+ blk_clear_queue_congested(pd->disk->queue, WRITE);
pkt->sleep_time = max(PACKET_WAIT_TIME, 1);
pkt_set_state(pkt, PACKET_WAITING_STATE);
@@ -1049,7 +1476,7 @@ static void pkt_start_write(struct pktcdvd_device *pd, struct packet_data *pkt)
for (f = 0; f < pkt->frames; f++)
if (!bio_add_page(pkt->w_bio, bvec[f].bv_page, CD_FRAMESIZE, bvec[f].bv_offset))
BUG();
- VPRINTK("pktcdvd: vcnt=%d\n", pkt->w_bio->bi_vcnt);
+ VPRINTK(DRIVER_NAME": vcnt=%d\n", pkt->w_bio->bi_vcnt);
atomic_set(&pkt->io_wait, 1);
pkt->w_bio->bi_rw = WRITE;
@@ -1286,7 +1713,7 @@ work_to_do:
static void pkt_print_settings(struct pktcdvd_device *pd)
{
- printk("pktcdvd: %s packets, ", pd->settings.fp ? "Fixed" : "Variable");
+ printk(DRIVER_NAME": %s packets, ", pd->settings.fp ? "Fixed" : "Variable");
printk("%u blocks, ", pd->settings.size >> 2);
printk("Mode-%c disc\n", pd->settings.block_mode == 8 ? '1' : '2');
}
@@ -1471,7 +1898,7 @@ static int pkt_set_write_settings(struct pktcdvd_device *pd)
/*
* paranoia
*/
- printk("pktcdvd: write mode wrong %d\n", wp->data_block_type);
+ printk(DRIVER_NAME": write mode wrong %d\n", wp->data_block_type);
return 1;
}
wp->packet_size = cpu_to_be32(pd->settings.size >> 2);
@@ -1515,7 +1942,7 @@ static int pkt_writable_track(struct pktcdvd_device *pd, track_information *ti)
if (ti->rt == 1 && ti->blank == 0)
return 1;
- printk("pktcdvd: bad state %d-%d-%d\n", ti->rt, ti->blank, ti->packet);
+ printk(DRIVER_NAME": bad state %d-%d-%d\n", ti->rt, ti->blank, ti->packet);
return 0;
}
@@ -1533,7 +1960,7 @@ static int pkt_writable_disc(struct pktcdvd_device *pd, disc_information *di)
case 0x12: /* DVD-RAM */
return 1;
default:
- VPRINTK("pktcdvd: Wrong disc profile (%x)\n", pd->mmc3_profile);
+ VPRINTK(DRIVER_NAME": Wrong disc profile (%x)\n", pd->mmc3_profile);
return 0;
}
@@ -1542,22 +1969,22 @@ static int pkt_writable_disc(struct pktcdvd_device *pd, disc_information *di)
* but i'm not sure, should we leave this to user apps? probably.
*/
if (di->disc_type == 0xff) {
- printk("pktcdvd: Unknown disc. No track?\n");
+ printk(DRIVER_NAME": Unknown disc. No track?\n");
return 0;
}
if (di->disc_type != 0x20 && di->disc_type != 0) {
- printk("pktcdvd: Wrong disc type (%x)\n", di->disc_type);
+ printk(DRIVER_NAME": Wrong disc type (%x)\n", di->disc_type);
return 0;
}
if (di->erasable == 0) {
- printk("pktcdvd: Disc not erasable\n");
+ printk(DRIVER_NAME": Disc not erasable\n");
return 0;
}
if (di->border_status == PACKET_SESSION_RESERVED) {
- printk("pktcdvd: Can't write to last track (reserved)\n");
+ printk(DRIVER_NAME": Can't write to last track (reserved)\n");
return 0;
}
@@ -1593,12 +2020,12 @@ static int pkt_probe_settings(struct pktcdvd_device *pd)
track = 1; /* (di.last_track_msb << 8) | di.last_track_lsb; */
if ((ret = pkt_get_track_info(pd, track, 1, &ti))) {
- printk("pktcdvd: failed get_track\n");
+ printk(DRIVER_NAME": failed get_track\n");
return ret;
}
if (!pkt_writable_track(pd, &ti)) {
- printk("pktcdvd: can't write to this track\n");
+ printk(DRIVER_NAME": can't write to this track\n");
return -EROFS;
}
@@ -1608,11 +2035,11 @@ static int pkt_probe_settings(struct pktcdvd_device *pd)
*/
pd->settings.size = be32_to_cpu(ti.fixed_packet_size) << 2;
if (pd->settings.size == 0) {
- printk("pktcdvd: detected zero packet size!\n");
+ printk(DRIVER_NAME": detected zero packet size!\n");
return -ENXIO;
}
if (pd->settings.size > PACKET_MAX_SECTORS) {
- printk("pktcdvd: packet size is too big\n");
+ printk(DRIVER_NAME": packet size is too big\n");
return -EROFS;
}
pd->settings.fp = ti.fp;
@@ -1654,7 +2081,7 @@ static int pkt_probe_settings(struct pktcdvd_device *pd)
pd->settings.block_mode = PACKET_BLOCK_MODE2;
break;
default:
- printk("pktcdvd: unknown data mode\n");
+ printk(DRIVER_NAME": unknown data mode\n");
return -EROFS;
}
return 0;
@@ -1688,10 +2115,10 @@ static int pkt_write_caching(struct pktcdvd_device *pd, int set)
cgc.buflen = cgc.cmd[8] = 2 + ((buf[0] << 8) | (buf[1] & 0xff));
ret = pkt_mode_select(pd, &cgc);
if (ret) {
- printk("pktcdvd: write caching control failed\n");
+ printk(DRIVER_NAME": write caching control failed\n");
pkt_dump_sense(&cgc);
} else if (!ret && set)
- printk("pktcdvd: enabled write caching on %s\n", pd->name);
+ printk(DRIVER_NAME": enabled write caching on %s\n", pd->name);
return ret;
}
@@ -1805,11 +2232,11 @@ static int pkt_media_speed(struct pktcdvd_device *pd, unsigned *speed)
}
if (!buf[6] & 0x40) {
- printk("pktcdvd: Disc type is not CD-RW\n");
+ printk(DRIVER_NAME": Disc type is not CD-RW\n");
return 1;
}
if (!buf[6] & 0x4) {
- printk("pktcdvd: A1 values on media are not valid, maybe not CDRW?\n");
+ printk(DRIVER_NAME": A1 values on media are not valid, maybe not CDRW?\n");
return 1;
}
@@ -1829,14 +2256,14 @@ static int pkt_media_speed(struct pktcdvd_device *pd, unsigned *speed)
*speed = us_clv_to_speed[sp];
break;
default:
- printk("pktcdvd: Unknown disc sub-type %d\n",st);
+ printk(DRIVER_NAME": Unknown disc sub-type %d\n",st);
return 1;
}
if (*speed) {
- printk("pktcdvd: Max. media speed: %d\n",*speed);
+ printk(DRIVER_NAME": Max. media speed: %d\n",*speed);
return 0;
} else {
- printk("pktcdvd: Unknown speed %d for sub-type %d\n",sp,st);
+ printk(DRIVER_NAME": Unknown speed %d for sub-type %d\n",sp,st);
return 1;
}
}
@@ -1847,7 +2274,7 @@ static int pkt_perform_opc(struct pktcdvd_device *pd)
struct request_sense sense;
int ret;
- VPRINTK("pktcdvd: Performing OPC\n");
+ VPRINTK(DRIVER_NAME": Performing OPC\n");
init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
cgc.sense = &sense;
@@ -1865,12 +2292,12 @@ static int pkt_open_write(struct pktcdvd_device *pd)
unsigned int write_speed, media_write_speed, read_speed;
if ((ret = pkt_probe_settings(pd))) {
- VPRINTK("pktcdvd: %s failed probe\n", pd->name);
+ VPRINTK(DRIVER_NAME": %s failed probe\n", pd->name);
return ret;
}
if ((ret = pkt_set_write_settings(pd))) {
- DPRINTK("pktcdvd: %s failed saving write settings\n", pd->name);
+ DPRINTK(DRIVER_NAME": %s failed saving write settings\n", pd->name);
return -EIO;
}
@@ -1882,26 +2309,26 @@ static int pkt_open_write(struct pktcdvd_device *pd)
case 0x13: /* DVD-RW */
case 0x1a: /* DVD+RW */
case 0x12: /* DVD-RAM */
- DPRINTK("pktcdvd: write speed %ukB/s\n", write_speed);
+ DPRINTK(DRIVER_NAME": write speed %ukB/s\n", write_speed);
break;
default:
if ((ret = pkt_media_speed(pd, &media_write_speed)))
media_write_speed = 16;
write_speed = min(write_speed, media_write_speed * 177);
- DPRINTK("pktcdvd: write speed %ux\n", write_speed / 176);
+ DPRINTK(DRIVER_NAME": write speed %ux\n", write_speed / 176);
break;
}
read_speed = write_speed;
if ((ret = pkt_set_speed(pd, write_speed, read_speed))) {
- DPRINTK("pktcdvd: %s couldn't set write speed\n", pd->name);
+ DPRINTK(DRIVER_NAME": %s couldn't set write speed\n", pd->name);
return -EIO;
}
pd->write_speed = write_speed;
pd->read_speed = read_speed;
if ((ret = pkt_perform_opc(pd))) {
- DPRINTK("pktcdvd: %s Optimum Power Calibration failed\n", pd->name);
+ DPRINTK(DRIVER_NAME": %s Optimum Power Calibration failed\n", pd->name);
}
return 0;
@@ -1929,7 +2356,7 @@ static int pkt_open_dev(struct pktcdvd_device *pd, int write)
goto out_putdev;
if ((ret = pkt_get_last_written(pd, &lba))) {
- printk("pktcdvd: pkt_get_last_written failed\n");
+ printk(DRIVER_NAME": pkt_get_last_written failed\n");
goto out_unclaim;
}
@@ -1959,11 +2386,11 @@ static int pkt_open_dev(struct pktcdvd_device *pd, int write)
if (write) {
if (!pkt_grow_pktlist(pd, CONFIG_CDROM_PKTCDVD_BUFFERS)) {
- printk("pktcdvd: not enough memory for buffers\n");
+ printk(DRIVER_NAME": not enough memory for buffers\n");
ret = -ENOMEM;
goto out_unclaim;
}
- printk("pktcdvd: %lukB available on disc\n", lba << 1);
+ printk(DRIVER_NAME": %lukB available on disc\n", lba << 1);
}
return 0;
@@ -1983,7 +2410,7 @@ out:
static void pkt_release_dev(struct pktcdvd_device *pd, int flush)
{
if (flush && pkt_flush_cache(pd))
- DPRINTK("pktcdvd: %s not flushing cache\n", pd->name);
+ DPRINTK(DRIVER_NAME": %s not flushing cache\n", pd->name);
pkt_lock_door(pd, 0);
@@ -2006,7 +2433,7 @@ static int pkt_open(struct inode *inode, struct file *file)
struct pktcdvd_device *pd = NULL;
int ret;
- VPRINTK("pktcdvd: entering open\n");
+ VPRINTK(DRIVER_NAME": entering open\n");
mutex_lock(&ctl_mutex);
pd = pkt_find_dev_from_minor(iminor(inode));
@@ -2040,7 +2467,7 @@ static int pkt_open(struct inode *inode, struct file *file)
out_dec:
pd->refcnt--;
out:
- VPRINTK("pktcdvd: failed open (%d)\n", ret);
+ VPRINTK(DRIVER_NAME": failed open (%d)\n", ret);
mutex_unlock(&ctl_mutex);
return ret;
}
@@ -2088,7 +2515,7 @@ static int pkt_make_request(request_queue_t *q, struct bio *bio)
pd = q->queuedata;
if (!pd) {
- printk("pktcdvd: %s incorrect request queue\n", bdevname(bio->bi_bdev, b));
+ printk(DRIVER_NAME": %s incorrect request queue\n", bdevname(bio->bi_bdev, b));
goto end_io;
}
@@ -2110,13 +2537,13 @@ static int pkt_make_request(request_queue_t *q, struct bio *bio)
}
if (!test_bit(PACKET_WRITABLE, &pd->flags)) {
- printk("pktcdvd: WRITE for ro device %s (%llu)\n",
+ printk(DRIVER_NAME": WRITE for ro device %s (%llu)\n",
pd->name, (unsigned long long)bio->bi_sector);
goto end_io;
}
if (!bio->bi_size || (bio->bi_size % CD_FRAMESIZE)) {
- printk("pktcdvd: wrong bio size\n");
+ printk(DRIVER_NAME": wrong bio size\n");
goto end_io;
}
@@ -2176,6 +2603,23 @@ static int pkt_make_request(request_queue_t *q, struct bio *bio)
}
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.
+ */
+ spin_lock(&pd->lock);
+ if (pd->write_congestion_on > 0
+ && pd->bio_queue_size >= pd->write_congestion_on) {
+ blk_set_queue_congested(q, WRITE);
+ do {
+ spin_unlock(&pd->lock);
+ congestion_wait(WRITE, HZ);
+ spin_lock(&pd->lock);
+ } while(pd->bio_queue_size > pd->write_congestion_off);
+ }
+ spin_unlock(&pd->lock);
+
/*
* No matching packet found. Store the bio in the work queue.
*/
@@ -2295,6 +2739,9 @@ static int pkt_seq_show(struct seq_file *m, void *p)
seq_printf(m, "\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]);
+ seq_printf(m, "\twrite congestion marks:\toff=%d on=%d\n",
+ pd->write_congestion_off,
+ pd->write_congestion_on);
return 0;
}
@@ -2319,7 +2766,7 @@ static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev)
struct block_device *bdev;
if (pd->pkt_dev == dev) {
- printk("pktcdvd: Recursive setup not allowed\n");
+ printk(DRIVER_NAME": Recursive setup not allowed\n");
return -EBUSY;
}
for (i = 0; i < MAX_WRITERS; i++) {
@@ -2327,11 +2774,11 @@ static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev)
if (!pd2)
continue;
if (pd2->bdev->bd_dev == dev) {
- printk("pktcdvd: %s already setup\n", bdevname(pd2->bdev, b));
+ printk(DRIVER_NAME": %s already setup\n", bdevname(pd2->bdev, b));
return -EBUSY;
}
if (pd2->pkt_dev == dev) {
- printk("pktcdvd: Can't chain pktcdvd devices\n");
+ printk(DRIVER_NAME": Can't chain pktcdvd devices\n");
return -EBUSY;
}
}
@@ -2354,7 +2801,7 @@ static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev)
atomic_set(&pd->cdrw.pending_bios, 0);
pd->cdrw.thread = kthread_run(kcdrwd, pd, "%s", pd->name);
if (IS_ERR(pd->cdrw.thread)) {
- printk("pktcdvd: can't start kernel thread\n");
+ printk(DRIVER_NAME": can't start kernel thread\n");
ret = -ENOMEM;
goto out_mem;
}
@@ -2364,7 +2811,7 @@ static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev)
proc->data = pd;
proc->proc_fops = &pkt_proc_fops;
}
- DPRINTK("pktcdvd: writer %s mapped to %s\n", pd->name, bdevname(bdev, b));
+ DPRINTK(DRIVER_NAME": writer %s mapped to %s\n", pd->name, bdevname(bdev, b));
return 0;
out_mem:
@@ -2401,7 +2848,7 @@ static int pkt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, u
return blkdev_ioctl(pd->bdev->bd_inode, file, cmd, arg);
default:
- VPRINTK("pktcdvd: Unknown ioctl for %s (%x)\n", pd->name, cmd);
+ VPRINTK(DRIVER_NAME": Unknown ioctl for %s (%x)\n", pd->name, cmd);
return -ENOTTY;
}
@@ -2434,51 +2881,55 @@ static struct block_device_operations pktcdvd_ops = {
/*
* Set up mapping from pktcdvd device to CD-ROM device.
*/
-static int pkt_setup_dev(struct pkt_ctrl_command *ctrl_cmd)
+static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev)
{
int idx;
int ret = -ENOMEM;
struct pktcdvd_device *pd;
struct gendisk *disk;
- dev_t dev = new_decode_dev(ctrl_cmd->dev);
+
+ mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
for (idx = 0; idx < MAX_WRITERS; idx++)
if (!pkt_devs[idx])
break;
if (idx == MAX_WRITERS) {
- printk("pktcdvd: max %d writers supported\n", MAX_WRITERS);
- return -EBUSY;
+ printk(DRIVER_NAME": max %d writers supported\n", MAX_WRITERS);
+ ret = -EBUSY;
+ goto out_mutex;
}
pd = kzalloc(sizeof(struct pktcdvd_device), GFP_KERNEL);
if (!pd)
- return ret;
+ goto out_mutex;
pd->rb_pool = mempool_create_kmalloc_pool(PKT_RB_POOL_SIZE,
sizeof(struct pkt_rb_node));
if (!pd->rb_pool)
goto out_mem;
- disk = alloc_disk(1);
- if (!disk)
- goto out_mem;
- pd->disk = disk;
-
INIT_LIST_HEAD(&pd->cdrw.pkt_free_list);
INIT_LIST_HEAD(&pd->cdrw.pkt_active_list);
spin_lock_init(&pd->cdrw.active_list_lock);
spin_lock_init(&pd->lock);
spin_lock_init(&pd->iosched.lock);
- sprintf(pd->name, "pktcdvd%d", idx);
+ sprintf(pd->name, DRIVER_NAME"%d", idx);
init_waitqueue_head(&pd->wqueue);
pd->bio_queue = RB_ROOT;
- disk->major = pkt_major;
+ pd->write_congestion_on = write_congestion_on;
+ pd->write_congestion_off = write_congestion_off;
+
+ disk = alloc_disk(1);
+ if (!disk)
+ goto out_mem;
+ pd->disk = disk;
+ disk->major = pktdev_major;
disk->first_minor = idx;
disk->fops = &pktcdvd_ops;
disk->flags = GENHD_FL_REMOVABLE;
- sprintf(disk->disk_name, "pktcdvd%d", idx);
+ strcpy(disk->disk_name, pd->name);
disk->private_data = pd;
disk->queue = blk_alloc_queue(GFP_KERNEL);
if (!disk->queue)
@@ -2490,8 +2941,15 @@ static int pkt_setup_dev(struct pkt_ctrl_command *ctrl_cmd)
goto out_new_dev;
add_disk(disk);
+
+ pkt_sysfs_dev_new(pd);
+ pkt_debugfs_dev_new(pd);
+
pkt_devs[idx] = pd;
- ctrl_cmd->pkt_dev = new_encode_dev(pd->pkt_dev);
+ if (pkt_dev)
+ *pkt_dev = pd->pkt_dev;
+
+ mutex_unlock(&ctl_mutex);
return 0;
out_new_dev:
@@ -2502,17 +2960,22 @@ out_mem:
if (pd->rb_pool)
mempool_destroy(pd->rb_pool);
kfree(pd);
+out_mutex:
+ mutex_unlock(&ctl_mutex);
+ printk(DRIVER_NAME": setup of pktcdvd device failed\n");
return ret;
}
/*
* Tear down mapping from pktcdvd device to CD-ROM device.
*/
-static int pkt_remove_dev(struct pkt_ctrl_command *ctrl_cmd)
+static int pkt_remove_dev(dev_t pkt_dev)
{
struct pktcdvd_device *pd;
int idx;
- dev_t pkt_dev = new_decode_dev(ctrl_cmd->pkt_dev);
+ int ret = 0;
+
+ mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
for (idx = 0; idx < MAX_WRITERS; idx++) {
pd = pkt_devs[idx];
@@ -2520,37 +2983,50 @@ static int pkt_remove_dev(struct pkt_ctrl_command *ctrl_cmd)
break;
}
if (idx == MAX_WRITERS) {
- DPRINTK("pktcdvd: dev not setup\n");
- return -ENXIO;
+ DPRINTK(DRIVER_NAME": dev not setup\n");
+ ret = -ENXIO;
+ goto out;
}
- if (pd->refcnt > 0)
- return -EBUSY;
-
+ if (pd->refcnt > 0) {
+ ret = -EBUSY;
+ goto out;
+ }
if (!IS_ERR(pd->cdrw.thread))
kthread_stop(pd->cdrw.thread);
+ pkt_devs[idx] = NULL;
+
+ pkt_debugfs_dev_remove(pd);
+ pkt_sysfs_dev_remove(pd);
+
blkdev_put(pd->bdev);
remove_proc_entry(pd->name, pkt_proc);
- DPRINTK("pktcdvd: writer %s unmapped\n", pd->name);
+ DPRINTK(DRIVER_NAME": writer %s unmapped\n", pd->name);
del_gendisk(pd->disk);
blk_cleanup_queue(pd->disk->queue);
put_disk(pd->disk);
- pkt_devs[idx] = NULL;
mempool_destroy(pd->rb_pool);
kfree(pd);
/* This is safe: open() is still holding a reference. */
module_put(THIS_MODULE);
- return 0;
+
+out:
+ mutex_unlock(&ctl_mutex);
+ return ret;
}
static void pkt_get_status(struct pkt_ctrl_command *ctrl_cmd)
{
- struct pktcdvd_device *pd = pkt_find_dev_from_minor(ctrl_cmd->dev_index);
+ struct pktcdvd_device *pd;
+
+ mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
+
+ pd = pkt_find_dev_from_minor(ctrl_cmd->dev_index);
if (pd) {
ctrl_cmd->dev = new_encode_dev(pd->bdev->bd_dev);
ctrl_cmd->pkt_dev = new_encode_dev(pd->pkt_dev);
@@ -2559,6 +3035,8 @@ static void pkt_get_status(struct pkt_ctrl_command *ctrl_cmd)
ctrl_cmd->pkt_dev = 0;
}
ctrl_cmd->num_devices = MAX_WRITERS;
+
+ mutex_unlock(&ctl_mutex);
}
static int pkt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
@@ -2566,6 +3044,7 @@ static int pkt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cm
void __user *argp = (void __user *)arg;
struct pkt_ctrl_command ctrl_cmd;
int ret = 0;
+ dev_t pkt_dev = 0;
if (cmd != PACKET_CTRL_CMD)
return -ENOTTY;
@@ -2577,21 +3056,16 @@ static int pkt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cm
case PKT_CTRL_CMD_SETUP:
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
- ret = pkt_setup_dev(&ctrl_cmd);
- mutex_unlock(&ctl_mutex);
+ ret = pkt_setup_dev(new_decode_dev(ctrl_cmd.dev), &pkt_dev);
+ ctrl_cmd.pkt_dev = new_encode_dev(pkt_dev);
break;
case PKT_CTRL_CMD_TEARDOWN:
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
- ret = pkt_remove_dev(&ctrl_cmd);
- mutex_unlock(&ctl_mutex);
+ ret = pkt_remove_dev(new_decode_dev(ctrl_cmd.pkt_dev));
break;
case PKT_CTRL_CMD_STATUS:
- mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
pkt_get_status(&ctrl_cmd);
- mutex_unlock(&ctl_mutex);
break;
default:
return -ENOTTY;
@@ -2610,7 +3084,7 @@ static struct file_operations pkt_ctl_fops = {
static struct miscdevice pkt_misc = {
.minor = MISC_DYNAMIC_MINOR,
- .name = "pktcdvd",
+ .name = DRIVER_NAME,
.fops = &pkt_ctl_fops
};
@@ -2618,33 +3092,42 @@ static int __init pkt_init(void)
{
int ret;
+ mutex_init(&ctl_mutex);
+
psd_pool = mempool_create_kmalloc_pool(PSD_POOL_SIZE,
sizeof(struct packet_stacked_data));
if (!psd_pool)
return -ENOMEM;
- ret = register_blkdev(pkt_major, "pktcdvd");
+ ret = register_blkdev(pktdev_major, DRIVER_NAME);
if (ret < 0) {
- printk("pktcdvd: Unable to register block device\n");
+ printk(DRIVER_NAME": Unable to register block device\n");
goto out2;
}
- if (!pkt_major)
- pkt_major = ret;
+ if (!pktdev_major)
+ pktdev_major = ret;
+
+ ret = pkt_sysfs_init();
+ if (ret)
+ goto out;
+
+ pkt_debugfs_init();
ret = misc_register(&pkt_misc);
if (ret) {
- printk("pktcdvd: Unable to register misc device\n");
- goto out;
+ printk(DRIVER_NAME": Unable to register misc device\n");
+ goto out_misc;
}
- mutex_init(&ctl_mutex);
-
- pkt_proc = proc_mkdir("pktcdvd", proc_root_driver);
+ pkt_proc = proc_mkdir(DRIVER_NAME, proc_root_driver);
return 0;
+out_misc:
+ pkt_debugfs_cleanup();
+ pkt_sysfs_cleanup();
out:
- unregister_blkdev(pkt_major, "pktcdvd");
+ unregister_blkdev(pktdev_major, DRIVER_NAME);
out2:
mempool_destroy(psd_pool);
return ret;
@@ -2652,9 +3135,13 @@ out2:
static void __exit pkt_exit(void)
{
- remove_proc_entry("pktcdvd", proc_root_driver);
+ remove_proc_entry(DRIVER_NAME, proc_root_driver);
misc_deregister(&pkt_misc);
- unregister_blkdev(pkt_major, "pktcdvd");
+
+ pkt_debugfs_cleanup();
+ pkt_sysfs_cleanup();
+
+ unregister_blkdev(pktdev_major, DRIVER_NAME);
mempool_destroy(psd_pool);
}