Skip to contentSkip to conversation

CDRW packet writing support for 2.6.7-bk13

PATCH In review Peter Osterlund 6 participants 27 messages Jul 1, 2004 – Jul 14, 2004

Subsystems (5)
Documentation VFS SCSI Block layer +1 more subsystems
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 CDRW packet writing support for 2.6.7-bk13

This patch implements CDRW packet writing as a kernel block device. Usage instructions are in the packet-writing.txt file.

A hint: If you don't want to wait for a complete disc format, you can format just a part of the disc. For example:

        cdrwtool -d /dev/hdc -m 10240

This will format 10240 blocks, ie 20MB.

---

 linux-petero/Documentation/cdrom/00-INDEX           |    2 
 linux-petero/Documentation/cdrom/packet-writing.txt |   22 
 linux-petero/drivers/block/Kconfig                  |   32 
 linux-petero/drivers/block/Makefile                 |    1 
 linux-petero/drivers/block/pktcdvd.c                | 2654 ++++++++++++++++++++
 linux-petero/drivers/cdrom/Makefile                 |    1 
 linux-petero/drivers/ide/ide-cd.c                   |    6 
 linux-petero/drivers/scsi/sr.c                      |    6 
 linux-petero/include/linux/cdrom.h                  |    1 
 linux-petero/include/linux/major.h                  |    2 
 linux-petero/include/linux/pktcdvd.h                |  261 +
 11 files changed, 2983 insertions(+), 5 deletions(-)
Documentation/cdrom/00-INDEX renamed from Documentation/cdrom/00-INDEX~packet-2.6.7 +2 -0
diff -puN Documentation/cdrom/00-INDEX~packet-2.6.7 Documentation/cdrom/00-INDEX
@@ -22,6 +22,8 @@ mcdx
 	- info on improved Mitsumi CD-ROM driver.
 optcd
 	- info on the Optics Storage 8000 AT CD-ROM driver
+packet-writing.txt
+	- Info on the CDRW packet writing module
 sbpcd
 	- info on the SoundBlaster/Panasonic CD-ROM interface driver.
 sjcd
Documentation/cdrom/packet-writing.txt new file +22 -0
diff -puN /dev/null Documentation/cdrom/packet-writing.txt
@@ -0,0 +1,22 @@
+Getting started quick
+---------------------
+
+- Select packet support in the block device section 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/
+
+- Grab a new CD-RW disc and format it (assuming CD-RW is hdc, substitute
+  as appropriate):
+	# cdrwtool -d /dev/hdc -q
+
+- Make sure that /dev/pktcdvd0 exists (mknod /dev/pktcdvd0 b 97 0)
+
+- Setup your writer
+	# pktsetup /dev/pktcdvd0 /dev/hdc
+
+- Now you can mount /dev/pktcdvd0 and copy files to it. Enjoy!
+	# mount /dev/pktcdvd0 /cdrom -t udf -o rw,noatime
drivers/block/Kconfig renamed from drivers/block/Kconfig~packet-2.6.7 +32 -0
diff -puN drivers/block/Kconfig~packet-2.6.7 drivers/block/Kconfig
@@ -340,6 +340,38 @@ config LBD
 	  your machine, or if you want to have a raid or loopback device
 	  bigger than 2TB.  Otherwise say N.
 
+config CDROM_PKTCDVD
+	tristate "Packet writing on CD/DVD media"
+	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.
+
+	  Currently only writing to CD-RW discs is possible.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called pktcdvd.
+
+config CDROM_PKTCDVD_BUFFERS
+	int "Free buffers for data gathering"
+	depends on CDROM_PKTCDVD
+	default "8"
+	help
+	  This controls the maximum number of active concurrent packets. More
+	  concurrent packets can increase write performance, but also require
+	  more memory. Each concurrent packet will require approximately 64Kb
+	  of non-swappable kernel memory, memory which will be allocated at
+	  pktsetup time.
+
+config CDROM_PKTCDVD_WCACHE
+	bool "Enable write caching"
+	depends on CDROM_PKTCDVD
+	help
+	  If enabled, write caching will be set for the CD-R/W device. For now
+	  this option is dangerous unless the CD-RW media is known good, as we
+	  don't do deferred write error handling yet.
+
 source "drivers/s390/block/Kconfig"
 
 endmenu
drivers/block/Makefile renamed from drivers/block/Makefile~packet-2.6.7 +1 -0
diff -puN drivers/block/Makefile~packet-2.6.7 drivers/block/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_BLK_DEV_XD)	+= xd.o
 obj-$(CONFIG_BLK_CPQ_DA)	+= cpqarray.o
 obj-$(CONFIG_BLK_CPQ_CISS_DA)  += cciss.o
 obj-$(CONFIG_BLK_DEV_DAC960)	+= DAC960.o
+obj-$(CONFIG_CDROM_PKTCDVD)	+= pktcdvd.o
 
 obj-$(CONFIG_BLK_DEV_UMEM)	+= umem.o
 obj-$(CONFIG_BLK_DEV_NBD)	+= nbd.o
drivers/block/pktcdvd.c new file +2654 -0

2,654 lines not on this page · raw message

drivers/cdrom/Makefile renamed from drivers/cdrom/Makefile~packet-2.6.7 +1 -0
diff -puN drivers/cdrom/Makefile~packet-2.6.7 drivers/cdrom/Makefile
@@ -8,6 +8,7 @@
 obj-$(CONFIG_BLK_DEV_IDECD)	+=              cdrom.o
 obj-$(CONFIG_BLK_DEV_SR)	+=              cdrom.o
 obj-$(CONFIG_PARIDE_PCD)	+=		cdrom.o
+obj-$(CONFIG_CDROM_PKTCDVD)	+=		cdrom.o
 
 obj-$(CONFIG_AZTCD)		+= aztcd.o
 obj-$(CONFIG_CDU31A)		+= cdu31a.o     cdrom.o
drivers/ide/ide-cd.c renamed from drivers/ide/ide-cd.c~packet-2.6.7 +4 -2
diff -puN drivers/ide/ide-cd.c~packet-2.6.7 drivers/ide/ide-cd.c
@@ -2003,7 +2003,7 @@ ide_do_rw_cdrom (ide_drive_t *drive, str
 			}
 			CDROM_CONFIG_FLAGS(drive)->seeking = 0;
 		}
-		if (IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap) {
+		if ((rq_data_dir(rq) == READ) && IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap) {
 			action = cdrom_start_seek(drive, block);
 		} else {
 			if (rq_data_dir(rq) == READ)
@@ -2960,8 +2960,10 @@ int ide_cdrom_probe_capabilities (ide_dr
 		CDROM_CONFIG_FLAGS(drive)->no_eject = 0;
 	if (cap.cd_r_write)
 		CDROM_CONFIG_FLAGS(drive)->cd_r = 1;
-	if (cap.cd_rw_write)
+	if (cap.cd_rw_write) {
 		CDROM_CONFIG_FLAGS(drive)->cd_rw = 1;
+		CDROM_CONFIG_FLAGS(drive)->ram = 1;
+	}
 	if (cap.test_write)
 		CDROM_CONFIG_FLAGS(drive)->test_write = 1;
 	if (cap.dvd_ram_read || cap.dvd_r_read || cap.dvd_rom)
drivers/scsi/sr.c renamed from drivers/scsi/sr.c~packet-2.6.7 +3 -3
diff -puN drivers/scsi/sr.c~packet-2.6.7 drivers/scsi/sr.c
@@ -880,10 +880,10 @@ static void get_capabilities(struct scsi
 		cd->cdi.mask |= CDC_CLOSE_TRAY; */
 
 	/*
-	 * if DVD-RAM of MRW-W, we are randomly writeable
+	 * if DVD-RAM, MRW-W or CD-RW, we are randomly writable
 	 */
-	if ((cd->cdi.mask & (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM)) !=
-			(CDC_DVD_RAM | CDC_MRW_W | CDC_RAM)) {
+	if ((cd->cdi.mask & (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) !=
+			(CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) {
 		cd->device->writeable = 1;
 		set_disk_ro(cd->disk, 0);
 	}
include/linux/cdrom.h renamed from include/linux/cdrom.h~packet-2.6.7 +1 -0
diff -puN include/linux/cdrom.h~packet-2.6.7 include/linux/cdrom.h
@@ -498,6 +498,7 @@ struct cdrom_generic_command
 #define GPMODE_VENDOR_PAGE		0x00
 #define GPMODE_R_W_ERROR_PAGE		0x01
 #define GPMODE_WRITE_PARMS_PAGE		0x05
+#define GPMODE_WCACHING_PAGE		0x08
 #define GPMODE_AUDIO_CTL_PAGE		0x0e
 #define GPMODE_POWER_PAGE		0x1a
 #define GPMODE_FAULT_FAIL_PAGE		0x1c
include/linux/major.h renamed from include/linux/major.h~packet-2.6.7 +2 -0
diff -puN include/linux/major.h~packet-2.6.7 include/linux/major.h
@@ -111,6 +111,8 @@
 
 #define MDISK_MAJOR		95
 
+#define PACKET_MAJOR		97
+
 #define UBD_MAJOR		98
 
 #define JSFD_MAJOR		99
include/linux/pktcdvd.h new file +261 -0

217 lines not on this page · raw message

The rest of this diff is not shown. View the raw message.

_

Signature · 2 lines
Peter Osterlund - [email protected]
http://w1.894.telia.com/~u89404340

Part of the diff is not shown here. View the raw message.

#1 lore raw
Quoted 2 lines · Peter Osterlund

This patch implements CDRW packet writing as a kernel block device. Usage instructions are in the packet-writing.txt file.

...

Review

Quoted 8 lines
(quoted diff) +8 -0
+static int pkt_proc_device(struct pktcdvd_device *pd, char *buf)
+{
+	char *b = buf, *msg;
+	char bdev_buf[BDEVNAME_SIZE];
+	int states[PACKET_NUM_STATES];
+
+	b += sprintf(b, "\nWriter %s mapped to %s:\n", pd->name,
+		     __bdevname(pd->dev, bdev_buf));

This code leaks a module reference. The patch below fixes it. I'm not sure it's correct, but do_open() also does module_put() after put_disk().

---

 linux-petero/fs/partitions/check.c |    1 +
 1 files changed, 1 insertion(+)
fs/partitions/check.c renamed from fs/partitions/check.c~packet-refcnt +1 -0
diff -puN fs/partitions/check.c~packet-refcnt fs/partitions/check.c
@@ -151,6 +151,7 @@ const char *__bdevname(dev_t dev, char *
 	if (disk) {
 		buffer = disk_name(disk, part, buffer);
 		put_disk(disk);
+		module_put(disk->fops->owner);
 	} else {
 		snprintf(buffer, BDEVNAME_SIZE, "unknown-block(%u,%u)",
 				MAJOR(dev), MINOR(dev));

_

Signature · 2 lines
Peter Osterlund - [email protected]
http://w1.894.telia.com/~u89404340
#2 lore raw
Quoted 16 lines · Peter Osterlund

This code leaks a module reference. The patch below fixes it. I'm not sure it's correct, but do_open() also does module_put() after put_disk().

---

 linux-petero/fs/partitions/check.c |    1 +
 1 files changed, 1 insertion(+)

diff -puN fs/partitions/check.c~packet-refcnt fs/partitions/check.c

fs/partitions/check.c const char *__bdevname(dev_t dev, char * · line 157

Quoted 7 lines
fs/partitions/check.c renamed from fs/partitions/check.c~packet-refcnt +1 -0
@@ -151,6 +151,7 @@ const char *__bdevname(dev_t dev, char *
 	if (disk) {
 		buffer = disk_name(disk, part, buffer);
 		put_disk(disk);
+		module_put(disk->fops->owner);
 	} else {
 		snprintf(buffer, BDEVNAME_SIZE, "unknown-block(%u,%u)",
 				MAJOR(dev), MINOR(dev));

Yes, there's certainly a leak there. It's surprising that it hasn't been noted before.

But:

const char *__bdevname(dev_t dev, char *buffer)

{
	struct gendisk *disk;
	int part;

	disk = get_gendisk(dev, &part);
	if (disk) {
		buffer = disk_name(disk, part, buffer);
		put_disk(disk);
	} else {
		snprintf(buffer, BDEVNAME_SIZE, "unknown-block(%u,%u)",
				MAJOR(dev), MINOR(dev));
	}

get_gendisk() did an internal module_get() in kobj_lookup(). It would seem to be logical that the module_put() should be in kobject_put(), called from put_disk(). But surely if we made that change 1000 things would explode.

Greg, Al?

#3 lore raw
Quoted 44 lines · Andrew Morton
Quoted 16 lines · Peter Osterlund

This code leaks a module reference. The patch below fixes it. I'm not sure it's correct, but do_open() also does module_put() after put_disk().

---

 linux-petero/fs/partitions/check.c |    1 +
 1 files changed, 1 insertion(+)

diff -puN fs/partitions/check.c~packet-refcnt fs/partitions/check.c

fs/partitions/check.c const char *__bdevname(dev_t dev, char * · line 157

Quoted 7 lines
fs/partitions/check.c renamed from fs/partitions/check.c~packet-refcnt +1 -0
@@ -151,6 +151,7 @@ const char *__bdevname(dev_t dev, char *
 	if (disk) {
 		buffer = disk_name(disk, part, buffer);
 		put_disk(disk);
+		module_put(disk->fops->owner);
 	} else {
 		snprintf(buffer, BDEVNAME_SIZE, "unknown-block(%u,%u)",
 				MAJOR(dev), MINOR(dev));

Yes, there's certainly a leak there. It's surprising that it hasn't been noted before.

But:

const char *__bdevname(dev_t dev, char *buffer)

{
	struct gendisk *disk;
	int part;

	disk = get_gendisk(dev, &part);
	if (disk) {
		buffer = disk_name(disk, part, buffer);
		put_disk(disk);
	} else {
		snprintf(buffer, BDEVNAME_SIZE, "unknown-block(%u,%u)",
				MAJOR(dev), MINOR(dev));
	}
Quoted 1 line

get_gendisk() did an internal module_get() in kobj_lookup().

But if kobj_lookup() succeeds, module_put() is called before returning (actually it's always called, right?)

Quoted 3 lines

It would seem to be logical that the module_put() should be in kobject_put(), called from put_disk(). But surely if we made that change 1000 things would explode.

I don't see how that would fix anything, as the module reference is not held after kobj_lookup() returns. Or am I missing something here?

thanks,

greg k-h

#4 lore raw
Quoted 15 lines · Greg KH
Quoted 15 lines

const char *__bdevname(dev_t dev, char *buffer)

{
	struct gendisk *disk;
	int part;

	disk = get_gendisk(dev, &part);
	if (disk) {
		buffer = disk_name(disk, part, buffer);
		put_disk(disk);
	} else {
		snprintf(buffer, BDEVNAME_SIZE, "unknown-block(%u,%u)",
				MAJOR(dev), MINOR(dev));
	}
Quoted 4 lines
Quoted 1 line

get_gendisk() did an internal module_get() in kobj_lookup().

But if kobj_lookup() succeeds, module_put() is called before returning (actually it's always called, right?)

Oop, sorry, yes. Peter, are you sure this is where the leak is coming from?

#5 lore raw
Quoted 16 lines · Andrew Morton
Quoted 15 lines · Greg KH
Quoted 15 lines

const char *__bdevname(dev_t dev, char *buffer)

{
	struct gendisk *disk;
	int part;

	disk = get_gendisk(dev, &part);
	if (disk) {
		buffer = disk_name(disk, part, buffer);
		put_disk(disk);
	} else {
		snprintf(buffer, BDEVNAME_SIZE, "unknown-block(%u,%u)",
				MAJOR(dev), MINOR(dev));
	}
Quoted 4 lines
Quoted 4 lines
Quoted 1 line

get_gendisk() did an internal module_get() in kobj_lookup().

But if kobj_lookup() succeeds, module_put() is called before returning (actually it's always called, right?)

But kobj_lookup() also calls probe(), which equals ata_probe() in the IDE case, and ata_probe() calls get_disk() which calls try_module_get() to acquire another reference to the module. I guess all probe() functions behave the same way, as I've seen the same problem with the sr_mod module.

Quoted 1 line

Oop, sorry, yes. Peter, are you sure this is where the leak is coming from?

I'm sure that the module reference count as reported by lsmod increases each time I access /proc/driver/pktcdvd/pktcdvd0. I can make this problem go away either by patching __bdevname() or by deleting the call to __bdevname() in pktcdvd.c.

Signature · 2 lines
Peter Osterlund - [email protected]
http://w1.894.telia.com/~u89404340
#6 lore raw
Quoted 7 lines · Peter Osterlund
Quoted 2 lines

Oop, sorry, yes. Peter, are you sure this is where the leak is coming from?

I'm sure that the module reference count as reported by lsmod increases each time I access /proc/driver/pktcdvd/pktcdvd0. I can make this problem go away either by patching __bdevname() or by deleting the call to __bdevname() in pktcdvd.c.

Can't you use bdevname(pd->bdev) in there?

#10 lore raw

PATCH 1/1 Fix open/close races in pktcdvd

In reply to Peter Osterlund

The handling of the pd->refcnt variable is racy in a number of places. For example, running:

while true ; do usleep 10 ; pktsetup /dev/pktcdvd0 /dev/hdc ; done & while true ; do pktsetup -d /dev/pktcdvd0 ; done

makes a pktsetup process get stuck in D state after a while.

This patch fixes it by introducing a mutex to protect the refcnt variable and critical sections in the open/release/setup/tear-down functions.

This patch applies cleanly on top of the "Fix race in pktcdvd kernel thread handling" patch.

---

 linux-petero/drivers/block/pktcdvd.c |   88 +++++++++++++++++++++--------------
 linux-petero/include/linux/pktcdvd.h |    3 -
 2 files changed, 55 insertions(+), 36 deletions(-)
drivers/block/pktcdvd.c renamed from drivers/block/pktcdvd.c~packet-open-race-fix +53 -35
diff -puN drivers/block/pktcdvd.c~packet-open-race-fix drivers/block/pktcdvd.c
@@ -1998,10 +1998,6 @@ static void pkt_release_dev(struct pktcd
 {
 	struct block_device *bdev;
 
-	atomic_dec(&pd->refcnt);
-	if (atomic_read(&pd->refcnt) > 0)
-		return;
-
 	bdev = bdget(pd->pkt_dev);
 	if (bdev) {
 		fsync_bdev(bdev);
@@ -2029,6 +2025,7 @@ static int pkt_open(struct inode *inode,
 	struct pktcdvd_device *pd = NULL;
 	struct block_device *pkt_bdev;
 	int ret;
+	int special_open, exclusive;
 
 	VPRINTK("pktcdvd: entering open\n");
 
@@ -2038,21 +2035,30 @@ static int pkt_open(struct inode *inode,
 		goto out;
 	}
 
+	special_open = 0;
+	if (((file->f_flags & O_ACCMODE) == O_RDONLY) && (file->f_flags & O_CREAT))
+		special_open = 1;
+
+	exclusive = 0;
+	if ((file->f_mode & FMODE_WRITE) || special_open)
+		exclusive = 1;
+
 	/*
 	 * either device is not configured, or pktsetup is old and doesn't
 	 * use O_CREAT to create device
 	 */
 	pd = &pkt_devs[iminor(inode)];
-	if (!pd->dev && !(file->f_flags & O_CREAT)) {
+	if (!pd->dev && !special_open) {
 		VPRINTK("pktcdvd: not configured and O_CREAT not set\n");
 		ret = -ENXIO;
 		goto out;
 	}
 
-	atomic_inc(&pd->refcnt);
-	if (atomic_read(&pd->refcnt) > 1) {
-		if (file->f_mode & FMODE_WRITE) {
-			VPRINTK("pktcdvd: busy open for write\n");
+	down(&pd->ctl_mutex);
+	pd->refcnt++;
+	if (pd->refcnt > 1) {
+		if (exclusive) {
+			VPRINTK("pktcdvd: busy open\n");
 			ret = -EBUSY;
 			goto out_dec;
 		}
@@ -2060,10 +2066,10 @@ static int pkt_open(struct inode *inode,
 		/*
 		 * Not first open, everything is already set up
 		 */
-		return 0;
+		goto done;
 	}
 
-	if (((file->f_flags & O_ACCMODE) != O_RDONLY) || !(file->f_flags & O_CREAT)) {
+	if (!special_open) {
 		if (pkt_open_dev(pd, file->f_mode & FMODE_WRITE)) {
 			ret = -EIO;
 			goto out_dec;
@@ -2079,16 +2085,20 @@ static int pkt_open(struct inode *inode,
 		set_blocksize(pkt_bdev, CD_FRAMESIZE);
 		bdput(pkt_bdev);
 	}
+
+done:
+	up(&pd->ctl_mutex);
 	return 0;
 
 out_dec:
-	atomic_dec(&pd->refcnt);
-	if (atomic_read(&pd->refcnt) == 0) {
+	pd->refcnt--;
+	if (pd->refcnt == 0) {
 		if (pd->bdev) {
 			blkdev_put(pd->bdev);
 			pd->bdev = NULL;
 		}
 	}
+	up(&pd->ctl_mutex);
 out:
 	VPRINTK("pktcdvd: failed open (%d)\n", ret);
 	return ret;
@@ -2099,11 +2109,17 @@ static int pkt_close(struct inode *inode
 	struct pktcdvd_device *pd = &pkt_devs[iminor(inode)];
 	int ret = 0;
 
+	down(&pd->ctl_mutex);
+	pd->refcnt--;
+	BUG_ON(pd->refcnt < 0);
+	if (pd->refcnt > 0)
+		goto out;
 	if (pd->dev) {
 		int flush = test_bit(PACKET_WRITABLE, &pd->flags);
 		pkt_release_dev(pd, flush);
 	}
-
+out:
+	up(&pd->ctl_mutex);
 	return ret;
 }
 
@@ -2332,7 +2348,7 @@ static int pkt_proc_device(struct pktcdv
 	b += sprintf(b, "\tread:\t\t\t%lukB\n", pd->stats.secs_r >> 1);
 
 	b += sprintf(b, "\nMisc:\n");
-	b += sprintf(b, "\treference count:\t%d\n", atomic_read(&pd->refcnt));
+	b += sprintf(b, "\treference count:\t%d\n", pd->refcnt);
 	b += sprintf(b, "\tflags:\t\t\t0x%lx\n", pd->flags);
 	b += sprintf(b, "\tread speed:\t\t%ukB/s\n", pd->read_speed * 150);
 	b += sprintf(b, "\twrite speed:\t\t%ukB/s\n", pd->write_speed * 150);
@@ -2371,8 +2387,6 @@ static int pkt_read_proc(char *page, cha
 	return len;
 }
 
-extern struct block_device_operations pktcdvd_ops;
-
 static int pkt_new_dev(struct pktcdvd_device *pd, struct block_device *bdev)
 {
 	dev_t dev = bdev->bd_dev;
@@ -2392,10 +2406,8 @@ static int pkt_new_dev(struct pktcdvd_de
 	/* This is safe, since we have a reference from open(). */
 	__module_get(THIS_MODULE);
 
-	memset(pd, 0, sizeof(struct pktcdvd_device));
+	memset(&pd->stats, 0, sizeof(struct packet_stats));
 
-	spin_lock_init(&pd->lock);
-	spin_lock_init(&pd->iosched.lock);
 	if (!pkt_grow_pktlist(pd, CONFIG_CDROM_PKTCDVD_BUFFERS)) {
 		printk("pktcdvd: not enough memory for buffers\n");
 		ret = -ENOMEM;
@@ -2404,12 +2416,7 @@ static int pkt_new_dev(struct pktcdvd_de
 
 	set_blocksize(bdev, CD_FRAMESIZE);
 	pd->dev = dev;
-	pd->bdev = NULL;
-	pd->pkt_dev = MKDEV(PACKET_MAJOR, minor);
-	sprintf(pd->name, "pktcdvd%d", minor);
-	atomic_set(&pd->refcnt, 0);
-	init_waitqueue_head(&pd->wqueue);
-	init_MUTEX_LOCKED(&pd->cdrw.thr_sem);
+	BUG_ON(pd->bdev);
 
 	pkt_init_queue(pd);
 
@@ -2428,7 +2435,6 @@ static int pkt_new_dev(struct pktcdvd_de
 
 out_thread:
 	pkt_shrink_pktlist(pd);
-	memset(pd, 0, sizeof(*pd));
 out_mem:
 	/* This is safe: open() is still holding a reference. */
 	module_put(THIS_MODULE);
@@ -2464,10 +2470,7 @@ static int pkt_setup_dev(struct pktcdvd_
 		printk("pktcdvd: Can't write to read-only dev\n");
 		goto out;
 	}
-	if ((ret = pkt_new_dev(pd, inode->i_bdev)))
-		goto out;
-
-	atomic_inc(&pd->refcnt);
+	ret = pkt_new_dev(pd, inode->i_bdev);
 
 out:
 	fput(file);
@@ -2502,8 +2505,8 @@ static int pkt_remove_dev(struct pktcdvd
 	pkt_shrink_pktlist(pd);
 
 	remove_proc_entry(pd->name, pkt_proc);
+	pd->dev = 0;
 	DPRINTK("pktcdvd: writer %d unmapped\n", pkt_get_minor(pd));
-	memset(pd, 0, sizeof(struct pktcdvd_device));
 	/* This is safe: open() is still holding a reference. */
 	module_put(THIS_MODULE);
 	return 0;
@@ -2527,6 +2530,7 @@ static int pkt_media_changed(struct gend
 static int pkt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
 	struct pktcdvd_device *pd = &pkt_devs[iminor(inode)];
+	int ret;
 
 	VPRINTK("pkt_ioctl: cmd %x, dev %d:%d\n", cmd, imajor(inode), iminor(inode));
 
@@ -2553,9 +2557,14 @@ static int pkt_ioctl(struct inode *inode
 	case PACKET_TEARDOWN_DEV:
 		if (!capable(CAP_SYS_ADMIN))
 			return -EPERM;
-		if (atomic_read(&pd->refcnt) != 1)
-			return -EBUSY;
-		return pkt_remove_dev(pd);
+		down(&pd->ctl_mutex);
+		BUG_ON(pd->refcnt < 1);
+		if (pd->refcnt != 1)
+			ret = -EBUSY;
+		else
+			ret = pkt_remove_dev(pd);
+		up(&pd->ctl_mutex);
+		return ret;
 
 	case BLKROSET:
 		if (capable(CAP_SYS_ADMIN))
@@ -2629,6 +2638,15 @@ int pkt_init(void)
 	for (i = 0; i < MAX_WRITERS; i++) {
 		struct pktcdvd_device *pd = &pkt_devs[i];
 		struct gendisk *disk = disks[i];
+
+		spin_lock_init(&pd->lock);
+		spin_lock_init(&pd->iosched.lock);
+		pd->pkt_dev = MKDEV(PACKET_MAJOR, i);
+		sprintf(pd->name, "pktcdvd%d", i);
+		init_waitqueue_head(&pd->wqueue);
+		init_MUTEX_LOCKED(&pd->cdrw.thr_sem);
+		init_MUTEX(&pd->ctl_mutex);
+
 		disk->major = PACKET_MAJOR;
 		disk->first_minor = i;
 		disk->fops = &pktcdvd_ops;
include/linux/pktcdvd.h renamed from include/linux/pktcdvd.h~packet-open-race-fix +2 -1
diff -puN include/linux/pktcdvd.h~packet-open-race-fix include/linux/pktcdvd.h
@@ -236,7 +236,8 @@ struct pktcdvd_device
 	char			name[20];
 	struct packet_settings	settings;
 	struct packet_stats	stats;
-	atomic_t		refcnt;
+	struct semaphore	ctl_mutex;	/* Serialize access to refcnt */
+	int			refcnt;		/* Open count */
 	__u8			write_speed;	/* current write speed */
 	__u8			read_speed;	/* current read speed */
 	unsigned long		offset;		/* start offset */

_

Signature · 2 lines
Peter Osterlund - [email protected]
http://w1.894.telia.com/~u89404340
#7 lore raw
In reply to Andrew Morton
Quoted 10 lines · Andrew Morton
Quoted 7 lines · Peter Osterlund
Quoted 2 lines

Oop, sorry, yes. Peter, are you sure this is where the leak is coming from?

I'm sure that the module reference count as reported by lsmod increases each time I access /proc/driver/pktcdvd/pktcdvd0. I can make this problem go away either by patching __bdevname() or by deleting the call to __bdevname() in pktcdvd.c.

Can't you use bdevname(pd->bdev) in there?

Not without more changes, because pd->bdev is only non-NULL when the device is actually opened by someone. When the device is setup (ie mapped to a real device) but not opened, pd->bdev (a struct block_device *) is NULL and pd->dev (a dev_t) is the only thing that keeps track of the mapping between the packet device and the real device.

Maybe that's a bad design though. Perhaps pd->dev can be eliminated altogether.

But anyway, if __bdevname() leaks a module reference it should get fixed, right?

Signature · 2 lines
Peter Osterlund - [email protected]
http://w1.894.telia.com/~u89404340
#11 lore raw

PATCH 1/1 Fix race in pktcdvd kernel thread handling

In reply to Peter Osterlund

Running "pktsetup -d" immediately after running "pktsetup" can deadlock, because if the kcdrwd thread hasn't flushed the pending signals before pkt_remove_dev() calls kill_proc(), kcdrwd() will not be woken up.

This patch fixes it by making sure the kcdrwd() thread has finished its initialization before the thread creator returns from pkt_new_dev().

---

 linux-petero/drivers/block/pktcdvd.c |    9 ++++++---
 linux-petero/include/linux/pktcdvd.h |    2 +-
 2 files changed, 7 insertions(+), 4 deletions(-)
drivers/block/pktcdvd.c renamed from drivers/block/pktcdvd.c~packet-kthread-race-fix +6 -3
diff -puN drivers/block/pktcdvd.c~packet-kthread-race-fix drivers/block/pktcdvd.c
@@ -1182,6 +1182,8 @@ static int kcdrwd(void *foobar)
 	siginitsetinv(&current->blocked, sigmask(SIGKILL));
 	flush_signals(current);
 
+	up(&pd->cdrw.thr_sem);
+
 	for (;;) {
 		DECLARE_WAITQUEUE(wait, current);
 
@@ -1276,7 +1278,7 @@ work_to_do:
 		pkt_iosched_process_queue(pd);
 	}
 
-	complete_and_exit(&pd->cdrw.thr_compl, 0);
+	up(&pd->cdrw.thr_sem);
 	return 0;
 }
 
@@ -2407,7 +2409,7 @@ static int pkt_new_dev(struct pktcdvd_de
 	sprintf(pd->name, "pktcdvd%d", minor);
 	atomic_set(&pd->refcnt, 0);
 	init_waitqueue_head(&pd->wqueue);
-	init_completion(&pd->cdrw.thr_compl);
+	init_MUTEX_LOCKED(&pd->cdrw.thr_sem);
 
 	pkt_init_queue(pd);
 
@@ -2418,6 +2420,7 @@ static int pkt_new_dev(struct pktcdvd_de
 		ret = -EBUSY;
 		goto out_thread;
 	}
+	down(&pd->cdrw.thr_sem);
 
 	create_proc_read_entry(pd->name, 0, pkt_proc, pkt_read_proc, pd);
 	DPRINTK("pktcdvd: writer %d mapped to %s\n", minor, bdevname(bdev, b));
@@ -2484,7 +2487,7 @@ static int pkt_remove_dev(struct pktcdvd
 			printk("pkt_remove_dev: can't kill kernel thread\n");
 			return ret;
 		}
-		wait_for_completion(&pd->cdrw.thr_compl);
+		down(&pd->cdrw.thr_sem);
 	}
 
 	/*
include/linux/pktcdvd.h renamed from include/linux/pktcdvd.h~packet-kthread-race-fix +1 -1
diff -puN include/linux/pktcdvd.h~packet-kthread-race-fix include/linux/pktcdvd.h
@@ -140,7 +140,7 @@ struct packet_cdrw
 	spinlock_t		active_list_lock; /* Serialize access to pkt_active_list */
 	pid_t			pid;
 	int			time_to_die;
-	struct completion	thr_compl;
+	struct semaphore	thr_sem;
 	elevator_merge_fn	*elv_merge_fn;
 	elevator_completed_req_fn *elv_completed_req_fn;
 	merge_requests_fn	*merge_requests_fn;

_

Signature · 2 lines
Peter Osterlund - [email protected]
http://w1.894.telia.com/~u89404340
#12 lore raw
In reply to Peter Osterlund
Quoted 2 lines

+ * - Generic interface for UDF to submit large packets for variable length
+ * packet writing

Huh, what's bad about bios?

Quoted 1 line

+#include <linux/buffer_head.h>

Where do you need buffer_head.h?

Quoted 1 line

+#define SCSI_IOCTL_SEND_COMMAND 1

Please include scsi_ioctl.h instead of duplicating it.

Quoted 1 line

+static struct gendisk *disks[MAX_WRITERS];

Please add a pointer to the gendisk to struct pktcdvd_device instead of a global array

drivers/block/pktcdvd.c line 71

Quoted 11 lines
drivers/block/pktcdvd.c new file +11 -0
+
+static struct pktcdvd_device *pkt_find_dev(request_queue_t *q)
+{
+	int i;
+
+	for (i = 0; i < MAX_WRITERS; i++)
+		if (pkt_devs[i].bdev && bdev_get_queue(pkt_devs[i].bdev) == q)
+			return &pkt_devs[i];
+
+	return NULL;
+}

Please just store the pktcdvd_device * in q->queuedata.

drivers/block/pktcdvd.c line 1180

Quoted 1 line
drivers/block/pktcdvd.c new file +1 -0
+	sprintf(current->comm, pd->name);

not needed, saemonize does it for you.

drivers/block/pktcdvd.c line 1876

Quoted 9 lines
drivers/block/pktcdvd.c new file +9 -0
+static int pkt_get_minor(struct pktcdvd_device *pd)
+{
+	int minor;
+	for (minor = 0; minor < MAX_WRITERS; minor++)
+		if (pd == &pkt_devs[minor])
+			break;
+	BUG_ON(minor == MAX_WRITERS);
+	return minor;
+}

Shouldn't be needed at all. Use an idr allocator to get a free minor in the setup, the actual I/O code shouldn't care about minors at all (if you follow my suggestions in the begining of this mail that should be taken care of)
and the actual code should never

drivers/block/pktcdvd.c line 1930

Quoted 5 lines
drivers/block/pktcdvd.c new file +5 -0
+	pd->cdrw.elv_merge_fn = q->elevator.elevator_merge_fn;
+	pd->cdrw.elv_completed_req_fn = q->elevator.elevator_completed_req_fn;
+	pd->cdrw.merge_requests_fn = q->merge_requests_fn;
+	q->elevator.elevator_merge_fn = pkt_lowlevel_elv_merge_fn;
+	q->elevator.elevator_completed_req_fn = pkt_lowlevel_elv_completed_req_fn;

This looks really really fishy. Playing with other drivers' elevator settings can't be safe. I'd say wait for the runtime selectable I/O scheduler that's planned for a while and add a special packetwriting scheduler that you switch to.

Quoted 4 lines
drivers/block/pktcdvd.c new file +4 -0
+/*
+ * called when the device is closed. makes sure that the device flushes
+ * the internal cache before we close.
+ */

drivers/block/pktcdvd.c line 1973

Quoted 10 lines
drivers/block/pktcdvd.c new file +10 -0
+static void pkt_release_dev(struct pktcdvd_device *pd, int flush)
+{
+	struct block_device *bdev;
+
+	atomic_dec(&pd->refcnt);
+	if (atomic_read(&pd->refcnt) > 0)
+		return;
+
+	bdev = bdget(pd->pkt_dev);
+	if (bdev) {

You reallu should keep a reference to the underlying bdev as long as you use it, aka bdev_get + blkdev_get in ->open, blkdev_put in ->release

drivers/block/pktcdvd.c line 1974

Quoted 1 line
drivers/block/pktcdvd.c new file +1 -0
+		fsync_bdev(bdev);

fs/block_dev.c already does a sync_blockdev() on last close, that should be enough.

drivers/block/pktcdvd.c line 2264

Quoted 2 lines
drivers/block/pktcdvd.c new file +2 -0
+static int pkt_proc_device(struct pktcdvd_device *pd, char *buf)
+{

seq_file interface please, or even better a one value per file sysfs interface.

drivers/block/pktcdvd.c line 2384

Quoted 1 line
drivers/block/pktcdvd.c new file +1 -0
+	pd->cdrw.pid = kernel_thread(kcdrwd, pd, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);

please use the kernel/ktread.c infastructure.

Quoted 6 lines
drivers/block/pktcdvd.c new file +6 -0
+static int pkt_setup_dev(struct pktcdvd_device *pd, unsigned int arg)
+{
+	struct inode *inode;
+	struct file *file;
+	int ret;
+

drivers/block/pktcdvd.c line 2422

Quoted 10 lines
drivers/block/pktcdvd.c new file +10 -0
+	if ((file = fget(arg)) == NULL) {
+		printk("pktcdvd: bad file descriptor passed\n");
+		return -EBADF;
+	}
+
+	ret = -EINVAL;
+	if ((inode = file->f_dentry->d_inode) == NULL) {
+		printk("pktcdvd: huh? file descriptor contains no inode?\n");
+		goto out;
+	}

If fget is successfull file->f_dentry->d_inode can't be NULL.

drivers/block/pktcdvd.c line 2535

Quoted 10 lines
drivers/block/pktcdvd.c new file +10 -0
+	case BLKROSET:
+		if (capable(CAP_SYS_ADMIN))
+			clear_bit(PACKET_WRITABLE, &pd->flags);
+	case BLKROGET:
+	case BLKSSZGET:
+	case BLKFLSBUF:
+		if (!pd->bdev)
+			return -ENXIO;
+		return -EINVAL;		    /* Handled by blkdev layer */
+

These aren't handled by drivers anyore in 2.6

Quoted 11 lines
drivers/block/pktcdvd.c new file +11 -0
+	for (i = 0; i < MAX_WRITERS; i++) {
+		disks[i] = alloc_disk(1);
+		if (!disks[i])
+			goto out_mem2;
+	}
+
+	for (i = 0; i < MAX_WRITERS; i++) {
+		struct pktcdvd_device *pd = &pkt_devs[i];
+		struct gendisk *disk = disks[i];
+		disk->major = PACKET_MAJOR;
+		disk->first_minor = i;

drivers/block/pktcdvd.c line 2609

Quoted 10 lines
drivers/block/pktcdvd.c new file +10 -0
+		disk->fops = &pktcdvd_ops;
+		disk->flags = GENHD_FL_REMOVABLE;
+		sprintf(disk->disk_name, "pktcdvd%d", i);
+		sprintf(disk->devfs_name, "pktcdvd/%d", i);
+		disk->private_data = pd;
+		disk->queue = blk_alloc_queue(GFP_KERNEL);
+		if (!disk->queue)
+			goto out_mem3;
+		add_disk(disk);
+	}

Please allocate all these on demand only when you actually attach a device.

All in all I really wonder whether a separate driver is really that a good fit for the functionality or whether it should be more integrated with the block layer, ala drivers/block/scsi_ioctl.c

#8 lore raw
In reply to Peter Osterlund
Quoted 3 lines · Peter Osterlund

But anyway, if __bdevname() leaks a module reference it should get
fixed, right?

Yes. The questions is, where's the bug? Who should be undoing the module_get() which happened via

	__bdevname
	->get_gendisk
	  ->kobj_lookup
	    ->ata_probe
	      ->get_disk
	        ->try_module_get

?

#9 lore raw
Quoted 6 lines · Andrew Morton
Quoted 3 lines · Peter Osterlund

But anyway, if __bdevname() leaks a module reference it should get
fixed, right?

Yes. The questions is, where's the bug?

in __bdevname. It shouldn't try the get_gendisk. I introduced that long ago, but it's wrong. Use bdevname where you can, else we can't do much else than printing the namjor/minor sanely.

The packet writing code should always use bdevname() (see my previous mail on how to get there), and here's a patch to fix __bdevname:

fs/partitions/check.c +4 -15
@@ -137,25 +137,14 @@
 EXPORT_SYMBOL(bdevname);
 
 /*
- * NOTE: this cannot be called from interrupt context.
- *
- * But in interrupt context you should really have a struct
- * block_device anyway and use bdevname() above.
+ * There's very little reason to use this, you should really
+ * have a struct block_device just about everywhere and use
+ * bdevname() instead.
  */
 const char *__bdevname(dev_t dev, char *buffer)
 {
-	struct gendisk *disk;
-	int part;
-
-	disk = get_gendisk(dev, &part);
-	if (disk) {
-		buffer = disk_name(disk, part, buffer);
-		put_disk(disk);
-	} else {
-		snprintf(buffer, BDEVNAME_SIZE, "unknown-block(%u,%u)",
+	snprintf(buffer, BDEVNAME_SIZE, "unknown-block(%u,%u)",
 				MAJOR(dev), MINOR(dev));
-	}
-
 	return buffer;
 }
 
#13 lore raw
In reply to Christoph Hellwig

Hi!

Thanks a lot for reviewing the patch.

Quoted 4 lines · Christoph Hellwig
Quoted 2 lines

+ * - Generic interface for UDF to submit large packets for variable length
+ * packet writing

Huh, what's bad about bios?

Nothing, that comment is a leftover from before bios existed. Fixed.

Quoted 3 lines
Quoted 1 line

+#include <linux/buffer_head.h>

Where do you need buffer_head.h?

For fsync_bdev(), which you say is not needed anyway. Also for the invalidate_bdev() call in pkt_remove_dev(). The loop driver is also calling invalidate_bdev() in its loop_clr_fd() function, so I guess that call is needed.

Quoted 3 lines
Quoted 1 line

+#define SCSI_IOCTL_SEND_COMMAND 1

Please include scsi_ioctl.h instead of duplicating it.

Done.

Quoted 4 lines
Quoted 1 line

+static struct gendisk *disks[MAX_WRITERS];

Please add a pointer to the gendisk to struct pktcdvd_device instead of a global array

Done.

Quoted 5 lines
Quoted 5 lines
(quoted diff) +5 -0
+
+static struct pktcdvd_device *pkt_find_dev(request_queue_t *q)
+{
+	int i;
+
Quoted 8 lines

Review

Quoted 6 lines
(quoted diff) +6 -0
+	for (i = 0; i < MAX_WRITERS; i++)
+		if (pkt_devs[i].bdev && bdev_get_queue(pkt_devs[i].bdev) == q)
+			return &pkt_devs[i];
+
+	return NULL;
+}

Please just store the pktcdvd_device * in q->queuedata.

I don't think that's possible, because q is the queue for the device this device is attached to, and the driver for that device might use queuedata for something else.

Quoted 3 lines

Review

Quoted 1 line
(quoted diff) +1 -0
+	sprintf(current->comm, pd->name);

not needed, saemonize does it for you.

Fixed.

Quoted 11 lines

Review

Quoted 9 lines
(quoted diff) +9 -0
+static int pkt_get_minor(struct pktcdvd_device *pd)
+{
+	int minor;
+	for (minor = 0; minor < MAX_WRITERS; minor++)
+		if (pd == &pkt_devs[minor])
+			break;
+	BUG_ON(minor == MAX_WRITERS);
+	return minor;
+}

Shouldn't be needed at all.

Removed.

Quoted 10 lines

Review

Quoted 5 lines
(quoted diff) +5 -0
+	pd->cdrw.elv_merge_fn = q->elevator.elevator_merge_fn;
+	pd->cdrw.elv_completed_req_fn = q->elevator.elevator_completed_req_fn;
+	pd->cdrw.merge_requests_fn = q->merge_requests_fn;
+	q->elevator.elevator_merge_fn = pkt_lowlevel_elv_merge_fn;
+	q->elevator.elevator_completed_req_fn = pkt_lowlevel_elv_completed_req_fn;

This looks really really fishy. Playing with other drivers' elevator settings can't be safe. I'd say wait for the runtime selectable I/O scheduler that's planned for a while and add a special packetwriting scheduler that you switch to.

OK, I'll wait. ;)

Quoted 9 lines
Quoted 9 lines
(quoted diff) +9 -0
+/*
+ * called when the device is closed. makes sure that the device flushes
+ * the internal cache before we close.
+ */
+static void pkt_release_dev(struct pktcdvd_device *pd, int flush)
+{
+	struct block_device *bdev;
+
+	atomic_dec(&pd->refcnt);
Quoted 8 lines

Review

Quoted 5 lines
(quoted diff) +5 -0
+	if (atomic_read(&pd->refcnt) > 0)
+		return;
+
+	bdev = bdget(pd->pkt_dev);
+	if (bdev) {

You reallu should keep a reference to the underlying bdev as long as you use it, aka bdev_get + blkdev_get in ->open, blkdev_put in ->release

I'll work on this tomorrow.

Quoted 4 lines

Review

Quoted 1 line
(quoted diff) +1 -0
+		fsync_bdev(bdev);

fs/block_dev.c already does a sync_blockdev() on last close, that should be enough.

OK, removed.

Quoted 5 lines

Review

Quoted 2 lines
(quoted diff) +2 -0
+static int pkt_proc_device(struct pktcdvd_device *pd, char *buf)
+{

seq_file interface please, or even better a one value per file sysfs interface.

I'll work on this tomorrow too.

Quoted 3 lines

Review

Quoted 1 line
(quoted diff) +1 -0
+	pd->cdrw.pid = kernel_thread(kcdrwd, pd, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);

please use the kernel/ktread.c infastructure.

Fixed.

Quoted 10 lines
Quoted 10 lines
(quoted diff) +10 -0
+static int pkt_setup_dev(struct pktcdvd_device *pd, unsigned int arg)
+{
+	struct inode *inode;
+	struct file *file;
+	int ret;
+
+	if ((file = fget(arg)) == NULL) {
+		printk("pktcdvd: bad file descriptor passed\n");
+		return -EBADF;
+	}
Quoted 8 lines

Review

Quoted 6 lines
(quoted diff) +6 -0
+
+	ret = -EINVAL;
+	if ((inode = file->f_dentry->d_inode) == NULL) {
+		printk("pktcdvd: huh? file descriptor contains no inode?\n");
+		goto out;
+	}

If fget is successfull file->f_dentry->d_inode can't be NULL.

Fixed.

Quoted 4 lines
Quoted 4 lines
(quoted diff) +4 -0
+	case BLKROSET:
+		if (capable(CAP_SYS_ADMIN))
+			clear_bit(PACKET_WRITABLE, &pd->flags);
+	case BLKROGET:
Quoted 8 lines

Review

Quoted 6 lines
(quoted diff) +6 -0
+	case BLKSSZGET:
+	case BLKFLSBUF:
+		if (!pd->bdev)
+			return -ENXIO;
+		return -EINVAL;		    /* Handled by blkdev layer */
+

These aren't handled by drivers anyore in 2.6

OK, removed.

Quoted 16 lines
Quoted 16 lines
(quoted diff) +16 -0
+	for (i = 0; i < MAX_WRITERS; i++) {
+		disks[i] = alloc_disk(1);
+		if (!disks[i])
+			goto out_mem2;
+	}
+
+	for (i = 0; i < MAX_WRITERS; i++) {
+		struct pktcdvd_device *pd = &pkt_devs[i];
+		struct gendisk *disk = disks[i];
+		disk->major = PACKET_MAJOR;
+		disk->first_minor = i;
+		disk->fops = &pktcdvd_ops;
+		disk->flags = GENHD_FL_REMOVABLE;
+		sprintf(disk->disk_name, "pktcdvd%d", i);
+		sprintf(disk->devfs_name, "pktcdvd/%d", i);
+		disk->private_data = pd;
Quoted 8 lines

Review

Quoted 5 lines
(quoted diff) +5 -0
+		disk->queue = blk_alloc_queue(GFP_KERNEL);
+		if (!disk->queue)
+			goto out_mem3;
+		add_disk(disk);
+	}

Please allocate all these on demand only when you actually attach a device.

But I need to open the device to be able to perform the ioctl to attach a device, and I can't open the device until add_disk() has been called. The loop device does the same thing.

Quoted 3 lines

All in all I really wonder whether a separate driver is really that a good fit for the functionality or whether it should be more integrated with the block layer, ala drivers/block/scsi_ioctl.c

Jens is probably better suited than me to comment on that.

Here is an incremental patch that goes on top all patches I've posted previously.

---------------

Various cleanups in the pktcdvd driver suggested by Christoph Hellwig.

- Removed obsolete comment.
- Don't redefine SCSI_IOCTL_SEND_COMMAND locally, use the proper #include instead.
- Removed the disks[] gendisk* array and added a gendisk pointer to struct pktcdvd_device instead.
- No need to set current->comm in kcdrwd, since daemonize does that by itself.
- No need to call fsync_bdev() in pkt_release_dev(), because it is handled by fs/block_dev.c.
- After a successful fget(), file->f_dentry->d_inode can't be NULL, so kill the useless check.
- The BLKROSET, BLKROGET, BLKSSZGET and BLKFLSBUF ioctls aren't handled by drivers any more in 2.6.
- Removed no longer needed function pkt_get_minor().
- Use the kernel/kthread.c infrastructure in the pktcdvd driver.

---

 linux-petero/drivers/block/pktcdvd.c |  117 ++++++++---------------------------
 linux-petero/include/linux/pktcdvd.h |    5 -
 2 files changed, 31 insertions(+), 91 deletions(-)
drivers/block/pktcdvd.c renamed from drivers/block/pktcdvd.c~packet-cleanup +29 -88
diff -puN drivers/block/pktcdvd.c~packet-cleanup drivers/block/pktcdvd.c
@@ -12,8 +12,6 @@
  * TODO: (circa order of when I will fix it)
  * - Only able to write on CD-RW media right now.
  * - check host application code on media and set it in write page
- * - Generic interface for UDF to submit large packets for variable length
- *   packet writing
  * - interface for UDF <-> packet to negotiate a new location when a write
  *   fails.
  * - handle OPC, especially for -RW media
@@ -40,24 +38,23 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/kthread.h>
 #include <linux/errno.h>
 #include <linux/spinlock.h>
 #include <linux/file.h>
 #include <linux/proc_fs.h>
-#include <linux/buffer_head.h>
+#include <linux/buffer_head.h>		/* for invalidate_bdev() */
 #include <linux/devfs_fs_kernel.h>
 #include <linux/suspend.h>
 #include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_ioctl.h>
 
 #include <asm/uaccess.h>
 
-#define SCSI_IOCTL_SEND_COMMAND	1
-
 #define ZONE(sector, pd) (((sector) + (pd)->offset) & ~((pd)->settings.size - 1))
 
 static struct pktcdvd_device *pkt_devs;
 static struct proc_dir_entry *pkt_proc;
-static struct gendisk *disks[MAX_WRITERS];
 
 static struct pktcdvd_device *pkt_find_dev(request_queue_t *q)
 {
@@ -1170,19 +1167,7 @@ static int kcdrwd(void *foobar)
 	struct packet_data *pkt;
 	long min_sleep_time, residue;
 
-	/*
-	 * exit_files, mm (move to lazy-tlb, so context switches are come
-	 * extremely cheap) etc
-	 */
-	daemonize(pd->name);
-
 	set_user_nice(current, -20);
-	sprintf(current->comm, pd->name);
-
-	siginitsetinv(&current->blocked, sigmask(SIGKILL));
-	flush_signals(current);
-
-	up(&pd->cdrw.thr_sem);
 
 	for (;;) {
 		DECLARE_WAITQUEUE(wait, current);
@@ -1250,14 +1235,14 @@ static int kcdrwd(void *foobar)
 			if (signal_pending(current)) {
 				flush_signals(current);
 			}
-			if (pd->cdrw.time_to_die)
+			if (kthread_should_stop())
 				break;
 		}
 work_to_do:
 		set_current_state(TASK_RUNNING);
 		remove_wait_queue(&pd->wqueue, &wait);
 
-		if (pd->cdrw.time_to_die)
+		if (kthread_should_stop())
 			break;
 
 		/*
@@ -1278,7 +1263,6 @@ work_to_do:
 		pkt_iosched_process_queue(pd);
 	}
 
-	up(&pd->cdrw.thr_sem);
 	return 0;
 }
 
@@ -1898,16 +1882,6 @@ static int pkt_open_write(struct pktcdvd
 	return 0;
 }
 
-static int pkt_get_minor(struct pktcdvd_device *pd)
-{
-	int minor;
-	for (minor = 0; minor < MAX_WRITERS; minor++)
-		if (pd == &pkt_devs[minor])
-			break;
-	BUG_ON(minor == MAX_WRITERS);
-	return minor;
-}
-
 /*
  * called at open time.
  */
@@ -1938,7 +1912,7 @@ static int pkt_open_dev(struct pktcdvd_d
 		return ret;
 	}
 
-	set_capacity(disks[pkt_get_minor(pd)], lba << 2);
+	set_capacity(pd->disk, lba << 2);
 
 	/*
 	 * The underlying block device needs to have its merge logic
@@ -1996,14 +1970,6 @@ restore_queue:
  */
 static void pkt_release_dev(struct pktcdvd_device *pd, int flush)
 {
-	struct block_device *bdev;
-
-	bdev = bdget(pd->pkt_dev);
-	if (bdev) {
-		fsync_bdev(bdev);
-		bdput(bdev);
-	}
-
 	if (flush && pkt_flush_cache(pd))
 		DPRINTK("pktcdvd: %s not flushing cache\n", pd->name);
 
@@ -2300,7 +2266,7 @@ static int pkt_merge_bvec(request_queue_
 
 static void pkt_init_queue(struct pktcdvd_device *pd)
 {
-	request_queue_t *q = disks[pkt_get_minor(pd)]->queue;
+	request_queue_t *q = pd->disk->queue;
 
 	blk_queue_make_request(q, pkt_make_request);
 	blk_queue_hardsect_size(q, CD_FRAMESIZE);
@@ -2390,19 +2356,17 @@ static int pkt_read_proc(char *page, cha
 static int pkt_new_dev(struct pktcdvd_device *pd, struct block_device *bdev)
 {
 	dev_t dev = bdev->bd_dev;
-	int minor;
+	int i;
 	int ret = 0;
 	char b[BDEVNAME_SIZE];
 
-	for (minor = 0; minor < MAX_WRITERS; minor++) {
-		if (pkt_devs[minor].dev == dev) {
+	for (i = 0; i < MAX_WRITERS; i++) {
+		if (pkt_devs[i].dev == dev) {
 			printk("pktcdvd: %s already setup\n", bdevname(bdev, b));
 			return -EBUSY;
 		}
 	}
 
-	minor = pkt_get_minor(pd);
-
 	/* This is safe, since we have a reference from open(). */
 	__module_get(THIS_MODULE);
 
@@ -2420,17 +2384,15 @@ static int pkt_new_dev(struct pktcdvd_de
 
 	pkt_init_queue(pd);
 
-	pd->cdrw.time_to_die = 0;
-	pd->cdrw.pid = kernel_thread(kcdrwd, pd, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
-	if (pd->cdrw.pid < 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");
-		ret = -EBUSY;
+		ret = -ENOMEM;
 		goto out_thread;
 	}
-	down(&pd->cdrw.thr_sem);
 
 	create_proc_read_entry(pd->name, 0, pkt_proc, pkt_read_proc, pd);
-	DPRINTK("pktcdvd: writer %d mapped to %s\n", minor, bdevname(bdev, b));
+	DPRINTK("pktcdvd: writer %s mapped to %s\n", pd->name, bdevname(bdev, b));
 	return 0;
 
 out_thread:
@@ -2455,11 +2417,7 @@ static int pkt_setup_dev(struct pktcdvd_
 		return -EBADF;
 	}
 
-	ret = -EINVAL;
-	if ((inode = file->f_dentry->d_inode) == NULL) {
-		printk("pktcdvd: huh? file descriptor contains no inode?\n");
-		goto out;
-	}
+	inode = file->f_dentry->d_inode;
 	ret = -ENOTBLK;
 	if (!S_ISBLK(inode->i_mode)) {
 		printk("pktcdvd: device is not a block device (duh)\n");
@@ -2480,18 +2438,9 @@ out:
 static int pkt_remove_dev(struct pktcdvd_device *pd)
 {
 	struct block_device *bdev;
-	int ret;
 
-	if (pd->cdrw.pid >= 0) {
-		pd->cdrw.time_to_die = 1;
-		wmb();
-		ret = kill_proc(pd->cdrw.pid, SIGKILL, 1);
-		if (ret) {
-			printk("pkt_remove_dev: can't kill kernel thread\n");
-			return ret;
-		}
-		down(&pd->cdrw.thr_sem);
-	}
+	if (!IS_ERR(pd->cdrw.thread))
+		kthread_stop(pd->cdrw.thread);
 
 	/*
 	 * will also invalidate buffers for CD-ROM
@@ -2506,7 +2455,7 @@ static int pkt_remove_dev(struct pktcdvd
 
 	remove_proc_entry(pd->name, pkt_proc);
 	pd->dev = 0;
-	DPRINTK("pktcdvd: writer %d unmapped\n", pkt_get_minor(pd));
+	DPRINTK("pktcdvd: writer %s unmapped\n", pd->name);
 	/* This is safe: open() is still holding a reference. */
 	module_put(THIS_MODULE);
 	return 0;
@@ -2566,16 +2515,6 @@ static int pkt_ioctl(struct inode *inode
 		up(&pd->ctl_mutex);
 		return ret;
 
-	case BLKROSET:
-		if (capable(CAP_SYS_ADMIN))
-			clear_bit(PACKET_WRITABLE, &pd->flags);
-	case BLKROGET:
-	case BLKSSZGET:
-	case BLKFLSBUF:
-		if (!pd->bdev)
-			return -ENXIO;
-		return -EINVAL;		    /* Handled by blkdev layer */
-
 	/*
 	 * forward selected CDROM ioctls to CD-ROM, for UDF
 	 */
@@ -2630,21 +2569,22 @@ int pkt_init(void)
 	memset(pkt_devs, 0, MAX_WRITERS * sizeof(struct pktcdvd_device));
 
 	for (i = 0; i < MAX_WRITERS; i++) {
-		disks[i] = alloc_disk(1);
-		if (!disks[i])
+		struct pktcdvd_device *pd = &pkt_devs[i];
+
+		pd->disk = alloc_disk(1);
+		if (!pd->disk)
 			goto out_mem2;
 	}
 
 	for (i = 0; i < MAX_WRITERS; i++) {
 		struct pktcdvd_device *pd = &pkt_devs[i];
-		struct gendisk *disk = disks[i];
+		struct gendisk *disk = pd->disk;
 
 		spin_lock_init(&pd->lock);
 		spin_lock_init(&pd->iosched.lock);
 		pd->pkt_dev = MKDEV(PACKET_MAJOR, i);
 		sprintf(pd->name, "pktcdvd%d", i);
 		init_waitqueue_head(&pd->wqueue);
-		init_MUTEX_LOCKED(&pd->cdrw.thr_sem);
 		init_MUTEX(&pd->ctl_mutex);
 
 		disk->major = PACKET_MAJOR;
@@ -2667,11 +2607,11 @@ int pkt_init(void)
 
 out_mem3:
 	while (i--)
-		blk_put_queue(disks[i]->queue);
+		blk_put_queue(pkt_devs[i].disk->queue);
 	i = MAX_WRITERS;
 out_mem2:
 	while (i--)
-		put_disk(disks[i]);
+		put_disk(pkt_devs[i].disk);
 	kfree(pkt_devs);
 out_mem:
 	printk("pktcdvd: out of memory\n");
@@ -2684,9 +2624,10 @@ void pkt_exit(void)
 {
 	int i;
 	for (i = 0; i < MAX_WRITERS; i++) {
-		del_gendisk(disks[i]);
-		blk_put_queue(disks[i]->queue);
-		put_disk(disks[i]);
+		struct gendisk *disk = pkt_devs[i].disk;
+		del_gendisk(disk);
+		blk_put_queue(disk->queue);
+		put_disk(disk);
 	}
 
 	devfs_remove("pktcdvd");
include/linux/pktcdvd.h renamed from include/linux/pktcdvd.h~packet-cleanup +2 -3
diff -puN include/linux/pktcdvd.h~packet-cleanup include/linux/pktcdvd.h
@@ -138,9 +138,7 @@ struct packet_cdrw
 	struct list_head	pkt_free_list;
 	struct list_head	pkt_active_list;
 	spinlock_t		active_list_lock; /* Serialize access to pkt_active_list */
-	pid_t			pid;
-	int			time_to_die;
-	struct semaphore	thr_sem;
+	struct task_struct	*thread;
 	elevator_merge_fn	*elv_merge_fn;
 	elevator_completed_req_fn *elv_completed_req_fn;
 	merge_requests_fn	*merge_requests_fn;
@@ -256,6 +254,7 @@ struct pktcdvd_device
 	atomic_t		scan_queue;	/* Set to non-zero when pkt_handle_queue */
 						/* needs to be run. */
 	struct packet_iosched   iosched;
+	struct gendisk		*disk;
 };
 
 #endif /* __KERNEL__ */

_

Signature · 2 lines
Peter Osterlund - [email protected]
http://w1.894.telia.com/~u89404340
#14 lore raw
Quoted 2 lines · Peter Osterlund

Here is an incremental patch that goes on top all patches I've posted previously.

And here is a combined patch consisting of the 7 patches I've posted so far, which should make it easier for those who want to test the driver. Applies to 2.6.7-bk15 and probably -bk16 and -bk17 too.

packet-2.6.7.patch
CDRW packet writing.

dvd+rw.patch
DVD+RW support

dvd-rw-packet.patch
Packet writing support for DVD-RW and DVD+RW discs.

packet-doc-update.patch
packet-doc-update

packet-kthread-race-fix.patch
packet-kthread-race-fix

packet-open-race-fix.patch
packet-open-race-fix

packet-cleanup.patch
packet-cleanup

unchanged:

Documentation/cdrom/00-INDEX renamed from Documentation/cdrom/00-INDEX~packet-2.6.7 +2 -0
@@ -22,6 +22,8 @@ mcdx
 	- info on improved Mitsumi CD-ROM driver.
 optcd
 	- info on the Optics Storage 8000 AT CD-ROM driver
+packet-writing.txt
+	- Info on the CDRW packet writing module
 sbpcd
 	- info on the SoundBlaster/Panasonic CD-ROM interface driver.
 sjcd

unchanged:

Documentation/cdrom/packet-writing.txt +88 -0

88 lines not on this page · raw message

unchanged:

drivers/block/Kconfig +33 -0
@@ -340,6 +340,39 @@
 	  your machine, or if you want to have a raid or loopback device
 	  bigger than 2TB.  Otherwise say N.
 
+config CDROM_PKTCDVD
+	tristate "Packet writing on CD/DVD media"
+	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.
+
+	  Currently only writing to CD-RW, DVD-RW and DVD+RW 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.
+
+config CDROM_PKTCDVD_BUFFERS
+	int "Free buffers for data gathering"
+	depends on CDROM_PKTCDVD
+	default "8"
+	help
+	  This controls the maximum number of active concurrent packets. More
+	  concurrent packets can increase write performance, but also require
+	  more memory. Each concurrent packet will require approximately 64Kb
+	  of non-swappable kernel memory, memory which will be allocated at
+	  pktsetup time.
+
+config CDROM_PKTCDVD_WCACHE
+	bool "Enable write caching"
+	depends on CDROM_PKTCDVD
+	help
+	  If enabled, write caching will be set for the CD-R/W device. For now
+	  this option is dangerous unless the CD-RW media is known good, as we
+	  don't do deferred write error handling yet.
+
 source "drivers/s390/block/Kconfig"
 
 endmenu

unchanged:

drivers/block/Makefile renamed from drivers/block/Makefile~packet-2.6.7 +1 -0
@@ -35,6 +35,7 @@ obj-$(CONFIG_BLK_DEV_XD)	+= xd.o
 obj-$(CONFIG_BLK_CPQ_DA)	+= cpqarray.o
 obj-$(CONFIG_BLK_CPQ_CISS_DA)  += cciss.o
 obj-$(CONFIG_BLK_DEV_DAC960)	+= DAC960.o
+obj-$(CONFIG_CDROM_PKTCDVD)	+= pktcdvd.o
 
 obj-$(CONFIG_BLK_DEV_UMEM)	+= umem.o
 obj-$(CONFIG_BLK_DEV_NBD)	+= nbd.o
drivers/block/pktcdvd.c +2647 -0

2,647 lines not on this page · raw message

unchanged:

drivers/cdrom/Makefile renamed from drivers/cdrom/Makefile~packet-2.6.7 +1 -0
@@ -8,6 +8,7 @@
 obj-$(CONFIG_BLK_DEV_IDECD)	+=              cdrom.o
 obj-$(CONFIG_BLK_DEV_SR)	+=              cdrom.o
 obj-$(CONFIG_PARIDE_PCD)	+=		cdrom.o
+obj-$(CONFIG_CDROM_PKTCDVD)	+=		cdrom.o
 
 obj-$(CONFIG_AZTCD)		+= aztcd.o
 obj-$(CONFIG_CDU31A)		+= cdu31a.o     cdrom.o

unchanged:

drivers/ide/ide-cd.c +6 -2
@@ -1940,6 +1940,8 @@
 	info->dma = drive->using_dma ? 1 : 0;
 	info->cmd = WRITE;
 
+	info->devinfo.media_written = 1;
+
 	/* Start sending the write request to the drive. */
 	return cdrom_start_packet_command(drive, 32768, cdrom_start_write_cont);
 }
@@ -2003,7 +2005,7 @@
 			}
 			CDROM_CONFIG_FLAGS(drive)->seeking = 0;
 		}
-		if (IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap) {
+		if ((rq_data_dir(rq) == READ) && IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap) {
 			action = cdrom_start_seek(drive, block);
 		} else {
 			if (rq_data_dir(rq) == READ)
@@ -2960,8 +2962,10 @@
 		CDROM_CONFIG_FLAGS(drive)->no_eject = 0;
 	if (cap.cd_r_write)
 		CDROM_CONFIG_FLAGS(drive)->cd_r = 1;
-	if (cap.cd_rw_write)
+	if (cap.cd_rw_write) {
 		CDROM_CONFIG_FLAGS(drive)->cd_rw = 1;
+		CDROM_CONFIG_FLAGS(drive)->ram = 1;
+	}
 	if (cap.test_write)
 		CDROM_CONFIG_FLAGS(drive)->test_write = 1;
 	if (cap.dvd_ram_read || cap.dvd_r_read || cap.dvd_rom)

unchanged:

drivers/scsi/sr.c +4 -3
@@ -379,6 +379,7 @@
 			return 0;
 		SCpnt->cmnd[0] = WRITE_10;
 		SCpnt->sc_data_direction = DMA_TO_DEVICE;
+ 	 	cd->cdi.media_written = 1;
 	} else if (rq_data_dir(SCpnt->request) == READ) {
 		SCpnt->cmnd[0] = READ_10;
 		SCpnt->sc_data_direction = DMA_FROM_DEVICE;
@@ -880,10 +881,10 @@
 		cd->cdi.mask |= CDC_CLOSE_TRAY; */
 
 	/*
-	 * if DVD-RAM of MRW-W, we are randomly writeable
+	 * if DVD-RAM, MRW-W or CD-RW, we are randomly writable
 	 */
-	if ((cd->cdi.mask & (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM)) !=
-			(CDC_DVD_RAM | CDC_MRW_W | CDC_RAM)) {
+	if ((cd->cdi.mask & (CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) !=
+			(CDC_DVD_RAM | CDC_MRW_W | CDC_RAM | CDC_CD_RW)) {
 		cd->device->writeable = 1;
 		set_disk_ro(cd->disk, 0);
 	}

unchanged:

include/linux/cdrom.h +3 -0
@@ -498,6 +498,7 @@
 #define GPMODE_VENDOR_PAGE		0x00
 #define GPMODE_R_W_ERROR_PAGE		0x01
 #define GPMODE_WRITE_PARMS_PAGE		0x05
+#define GPMODE_WCACHING_PAGE		0x08
 #define GPMODE_AUDIO_CTL_PAGE		0x0e
 #define GPMODE_POWER_PAGE		0x1a
 #define GPMODE_FAULT_FAIL_PAGE		0x1c
@@ -946,6 +947,8 @@
         __u8 reserved		: 6;	/* not used yet */
 	int cdda_method;		/* see flags */
 	__u8 last_sense;
+	__u8 media_written;		/* dirty flag, DVD+RW bookkeeping */
+	unsigned short mmc3_profile;	/* current MMC3 profile */
 	int for_data;
 	int (*exit)(struct cdrom_device_info *);
 	int mrw_mode_page;

unchanged:

include/linux/major.h renamed from include/linux/major.h~packet-2.6.7 +2 -0
@@ -111,6 +111,8 @@
 
 #define MDISK_MAJOR		95
 
+#define PACKET_MAJOR		97
+
 #define UBD_MAJOR		98
 
 #define JSFD_MAJOR		99
include/linux/pktcdvd.h +262 -0

134 lines not on this page · raw message

The rest of this diff is not shown. View the raw message.

unchanged:

drivers/cdrom/cdrom.c renamed from drivers/cdrom/cdrom.c~dvd+rw +80 -0

80 lines not on this page · raw message

The rest of this diff is not shown. View the raw message.

Signature · 2 lines
Peter Osterlund - [email protected]
http://w1.894.telia.com/~u89404340

Part of the diff is not shown here. View the raw message.

#23 lore raw
In reply to Peter Osterlund
Quoted 5 lines · Peter Osterlund
Quoted 3 lines

All in all I really wonder whether a separate driver is really that a good fit for the functionality or whether it should be more integrated with the block layer, ala drivers/block/scsi_ioctl.c

Jens is probably better suited than me to comment on that.

That would by far be the superior approach. The data gathering really does belong in the fs (it's just a larger fs block size) and would rid the driver of the elevator hacks it's currently using. Plus all the data gathering code, which has been notoriously buggy and hard to get right and deadlock free (in the past at least for 2.4, I haven't worked on this for a long time so it's not a comment on 2.6 code quality).

That would basically allow the merge of the remaining setup code into cdrom.c. It needs someone to do the actual UDF work though... The upside is that you would get automatic support for other devices as well, such as the 8KiB UDO.

Ideally, someone would add support for bigger PAGE_CACHE_SIZE :)

Signature · 1 line
Jens Axboe
#24 lore raw
In reply to Peter Osterlund
Quoted 4 lines · Peter Osterlund

For fsync_bdev(), which you say is not needed anyway. Also for the invalidate_bdev() call in pkt_remove_dev(). The loop driver is also calling invalidate_bdev() in its loop_clr_fd() function, so I guess that call is needed.

Only as long as it's reusing the same gendisk for different devices, more on that below.

Quoted 19 lines
Quoted 10 lines
Quoted 10 lines
(quoted diff) +10 -0
+	for (i = 0; i < MAX_WRITERS; i++) {
+		struct pktcdvd_device *pd = &pkt_devs[i];
+		struct gendisk *disk = disks[i];
+		disk->major = PACKET_MAJOR;
+		disk->first_minor = i;
+		disk->fops = &pktcdvd_ops;
+		disk->flags = GENHD_FL_REMOVABLE;
+		sprintf(disk->disk_name, "pktcdvd%d", i);
+		sprintf(disk->devfs_name, "pktcdvd/%d", i);
+		disk->private_data = pd;
Quoted 8 lines

Review

Quoted 5 lines
(quoted diff) +5 -0
+		disk->queue = blk_alloc_queue(GFP_KERNEL);
+		if (!disk->queue)
+			goto out_mem3;
+		add_disk(disk);
+	}

Please allocate all these on demand only when you actually attach a device.

Quoted 3 lines

But I need to open the device to be able to perform the ioctl to attach a device, and I can't open the device until add_disk() has been called. The loop device does the same thing.

And loops is broken the same way ;-) This I need an blockdevice to actually attach it scheme breaks in many ways, like the need to reset a gendisk instead of allocating a new one, lots of warts in ->open and problems with udev. Just keep a single character device around for all administrative work (like device mapper) - otoh if packet writing really moves to the block layer all this is magically fixed..

#25 lore raw
In reply to Peter Osterlund
Quoted 9 lines · Peter Osterlund
Quoted 5 lines · Christoph Hellwig

Review

Quoted 2 lines
(quoted diff) +2 -0
+static int pkt_proc_device(struct pktcdvd_device *pd, char *buf)
+{

seq_file interface please, or even better a one value per file sysfs interface.

I'll work on this tomorrow too.

I don't know if there's any point converting this to the sysfs interface, since the proc file only contains a bunch of read-only values which are mostly only useful for debugging.

Anyway, here is a patch to convert it to seq_file.

-----------------

Convert the /proc code in the pktcdvd driver to use the seq_file interface.

---

 linux-petero/drivers/block/pktcdvd.c |   97 ++++++++++++++++-------------------
 1 files changed, 47 insertions(+), 50 deletions(-)
drivers/block/pktcdvd.c renamed from drivers/block/pktcdvd.c~packet-seqfile +47 -50
diff -puN drivers/block/pktcdvd.c~packet-seqfile drivers/block/pktcdvd.c
@@ -43,6 +43,7 @@
 #include <linux/spinlock.h>
 #include <linux/file.h>
 #include <linux/proc_fs.h>
+#include <linux/seq_file.h>
 #include <linux/buffer_head.h>		/* for invalidate_bdev() */
 #include <linux/devfs_fs_kernel.h>
 #include <linux/suspend.h>
@@ -2275,28 +2276,29 @@ static void pkt_init_queue(struct pktcdv
 	q->queuedata = pd;
 }
 
-static int pkt_proc_device(struct pktcdvd_device *pd, char *buf)
+static int pkt_seq_show(struct seq_file *m, void *p)
 {
-	char *b = buf, *msg;
+	struct pktcdvd_device *pd = m->private;
+	char *msg;
 	char bdev_buf[BDEVNAME_SIZE];
 	int states[PACKET_NUM_STATES];
 
-	b += sprintf(b, "\nWriter %s mapped to %s:\n", pd->name,
-		     __bdevname(pd->dev, bdev_buf));
+	seq_printf(m, "Writer %s mapped to %s:\n", pd->name,
+		   __bdevname(pd->dev, bdev_buf));
 
-	b += sprintf(b, "\nSettings:\n");
-	b += sprintf(b, "\tpacket size:\t\t%dkB\n", pd->settings.size / 2);
+	seq_printf(m, "\nSettings:\n");
+	seq_printf(m, "\tpacket size:\t\t%dkB\n", pd->settings.size / 2);
 
 	if (pd->settings.write_type == 0)
 		msg = "Packet";
 	else
 		msg = "Unknown";
-	b += sprintf(b, "\twrite type:\t\t%s\n", msg);
+	seq_printf(m, "\twrite type:\t\t%s\n", msg);
 
-	b += sprintf(b, "\tpacket type:\t\t%s\n", pd->settings.fp ? "Fixed" : "Variable");
-	b += sprintf(b, "\tlink loss:\t\t%d\n", pd->settings.link_loss);
+	seq_printf(m, "\tpacket type:\t\t%s\n", pd->settings.fp ? "Fixed" : "Variable");
+	seq_printf(m, "\tlink loss:\t\t%d\n", pd->settings.link_loss);
 
-	b += sprintf(b, "\ttrack mode:\t\t%d\n", pd->settings.track_mode);
+	seq_printf(m, "\ttrack mode:\t\t%d\n", pd->settings.track_mode);
 
 	if (pd->settings.block_mode == PACKET_BLOCK_MODE1)
 		msg = "Mode 1";
@@ -2304,61 +2306,52 @@ static int pkt_proc_device(struct pktcdv
 		msg = "Mode 2";
 	else
 		msg = "Unknown";
-	b += sprintf(b, "\tblock mode:\t\t%s\n", msg);
+	seq_printf(m, "\tblock mode:\t\t%s\n", msg);
 
-	b += sprintf(b, "\nStatistics:\n");
-	b += sprintf(b, "\tpackets started:\t%lu\n", pd->stats.pkt_started);
-	b += sprintf(b, "\tpackets ended:\t\t%lu\n", pd->stats.pkt_ended);
-	b += sprintf(b, "\twritten:\t\t%lukB\n", pd->stats.secs_w >> 1);
-	b += sprintf(b, "\tread gather:\t\t%lukB\n", pd->stats.secs_rg >> 1);
-	b += sprintf(b, "\tread:\t\t\t%lukB\n", pd->stats.secs_r >> 1);
-
-	b += sprintf(b, "\nMisc:\n");
-	b += sprintf(b, "\treference count:\t%d\n", pd->refcnt);
-	b += sprintf(b, "\tflags:\t\t\t0x%lx\n", pd->flags);
-	b += sprintf(b, "\tread speed:\t\t%ukB/s\n", pd->read_speed * 150);
-	b += sprintf(b, "\twrite speed:\t\t%ukB/s\n", pd->write_speed * 150);
-	b += sprintf(b, "\tstart offset:\t\t%lu\n", pd->offset);
-	b += sprintf(b, "\tmode page offset:\t%u\n", pd->mode_offset);
+	seq_printf(m, "\nStatistics:\n");
+	seq_printf(m, "\tpackets started:\t%lu\n", pd->stats.pkt_started);
+	seq_printf(m, "\tpackets ended:\t\t%lu\n", pd->stats.pkt_ended);
+	seq_printf(m, "\twritten:\t\t%lukB\n", pd->stats.secs_w >> 1);
+	seq_printf(m, "\tread gather:\t\t%lukB\n", pd->stats.secs_rg >> 1);
+	seq_printf(m, "\tread:\t\t\t%lukB\n", pd->stats.secs_r >> 1);
+
+	seq_printf(m, "\nMisc:\n");
+	seq_printf(m, "\treference count:\t%d\n", pd->refcnt);
+	seq_printf(m, "\tflags:\t\t\t0x%lx\n", pd->flags);
+	seq_printf(m, "\tread speed:\t\t%ukB/s\n", pd->read_speed * 150);
+	seq_printf(m, "\twrite speed:\t\t%ukB/s\n", pd->write_speed * 150);
+	seq_printf(m, "\tstart offset:\t\t%lu\n", pd->offset);
+	seq_printf(m, "\tmode page offset:\t%u\n", pd->mode_offset);
 
-	b += sprintf(b, "\nQueue state:\n");
-	b += sprintf(b, "\tbios queued:\t\t%s\n", pd->bio_queue ? "yes" : "no");
+	seq_printf(m, "\nQueue state:\n");
+	seq_printf(m, "\tbios queued:\t\t%s\n", pd->bio_queue ? "yes" : "no");
 
 	pkt_count_states(pd, states);
-	b += sprintf(b, "\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, "\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]);
 
-	return b - buf;
+	return 0;
 }
 
-static int pkt_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
+static int pkt_seq_open(struct inode *inode, struct file *file)
 {
-	struct pktcdvd_device *pd = data;
-	char *buf = page;
-	int len;
-
-	len = pkt_proc_device(pd, buf);
-	buf += len;
-
-	if (len <= off + count)
-		*eof = 1;
-
-	*start = page + off;
-	len -= off;
-	if (len > count)
-		len = count;
-	if (len < 0)
-		len = 0;
-
-	return len;
+	return single_open(file, pkt_seq_show, PDE(inode)->data);
 }
 
+static struct file_operations pkt_proc_fops = {
+	.open	= pkt_seq_open,
+	.read	= seq_read,
+	.llseek	= seq_lseek,
+	.release = single_release
+};
+
 static int pkt_new_dev(struct pktcdvd_device *pd, struct block_device *bdev)
 {
 	dev_t dev = bdev->bd_dev;
 	int i;
 	int ret = 0;
 	char b[BDEVNAME_SIZE];
+	struct proc_dir_entry *proc;
 
 	for (i = 0; i < MAX_WRITERS; i++) {
 		if (pkt_devs[i].dev == dev) {
@@ -2391,7 +2384,11 @@ static int pkt_new_dev(struct pktcdvd_de
 		goto out_thread;
 	}
 
-	create_proc_read_entry(pd->name, 0, pkt_proc, pkt_read_proc, pd);
+	proc = create_proc_entry(pd->name, 0, pkt_proc);
+	if (proc) {
+		proc->data = pd;
+		proc->proc_fops = &pkt_proc_fops;
+	}
 	DPRINTK("pktcdvd: writer %s mapped to %s\n", pd->name, bdevname(bdev, b));
 	return 0;
 

_

Signature · 2 lines
Peter Osterlund - [email protected]
http://w1.894.telia.com/~u89404340
#26 lore raw
In reply to Christoph Hellwig
Quoted 5 lines · Christoph Hellwig
Quoted 5 lines
(quoted diff) +5 -0
+static void pkt_release_dev(struct pktcdvd_device *pd, int flush)
+{
+	struct block_device *bdev;
+
+	atomic_dec(&pd->refcnt);
Quoted 8 lines

Review

Quoted 5 lines
(quoted diff) +5 -0
+	if (atomic_read(&pd->refcnt) > 0)
+		return;
+
+	bdev = bdget(pd->pkt_dev);
+	if (bdev) {

You reallu should keep a reference to the underlying bdev as long as you use it

Remove pkt_dev from struct pktcdvd_device in the pktcdvd driver and remove unnecessary calls to bdget(). Suggested by Christoph Hellwig.

---

 linux-petero/drivers/block/pktcdvd.c |   20 ++++----------------
 linux-petero/include/linux/pktcdvd.h |    1 -
 2 files changed, 4 insertions(+), 17 deletions(-)
drivers/block/pktcdvd.c renamed from drivers/block/pktcdvd.c~packet-bdev +4 -16
diff -puN drivers/block/pktcdvd.c~packet-bdev drivers/block/pktcdvd.c
@@ -1990,7 +1990,6 @@ static void pkt_release_dev(struct pktcd
 static int pkt_open(struct inode *inode, struct file *file)
 {
 	struct pktcdvd_device *pd = NULL;
-	struct block_device *pkt_bdev;
 	int ret;
 	int special_open, exclusive;
 
@@ -2047,11 +2046,7 @@ static int pkt_open(struct inode *inode,
 	 * needed here as well, since ext2 (among others) may change
 	 * the blocksize at mount time
 	 */
-	pkt_bdev = bdget(inode->i_rdev);
-	if (pkt_bdev) {
-		set_blocksize(pkt_bdev, CD_FRAMESIZE);
-		bdput(pkt_bdev);
-	}
+	set_blocksize(inode->i_bdev, CD_FRAMESIZE);
 
 done:
 	up(&pd->ctl_mutex);
@@ -2432,21 +2427,15 @@ out:
 	return ret;
 }
 
-static int pkt_remove_dev(struct pktcdvd_device *pd)
+static int pkt_remove_dev(struct pktcdvd_device *pd, struct block_device *bdev)
 {
-	struct block_device *bdev;
-
 	if (!IS_ERR(pd->cdrw.thread))
 		kthread_stop(pd->cdrw.thread);
 
 	/*
 	 * will also invalidate buffers for CD-ROM
 	 */
-	bdev = bdget(pd->pkt_dev);
-	if (bdev) {
-		invalidate_bdev(bdev, 1);
-		bdput(bdev);
-	}
+	invalidate_bdev(bdev, 1);
 
 	pkt_shrink_pktlist(pd);
 
@@ -2508,7 +2497,7 @@ static int pkt_ioctl(struct inode *inode
 		if (pd->refcnt != 1)
 			ret = -EBUSY;
 		else
-			ret = pkt_remove_dev(pd);
+			ret = pkt_remove_dev(pd, inode->i_bdev);
 		up(&pd->ctl_mutex);
 		return ret;
 
@@ -2579,7 +2568,6 @@ int pkt_init(void)
 
 		spin_lock_init(&pd->lock);
 		spin_lock_init(&pd->iosched.lock);
-		pd->pkt_dev = MKDEV(PACKET_MAJOR, i);
 		sprintf(pd->name, "pktcdvd%d", i);
 		init_waitqueue_head(&pd->wqueue);
 		init_MUTEX(&pd->ctl_mutex);
include/linux/pktcdvd.h renamed from include/linux/pktcdvd.h~packet-bdev +0 -1
diff -puN include/linux/pktcdvd.h~packet-bdev include/linux/pktcdvd.h
@@ -230,7 +230,6 @@ struct pktcdvd_device
 {
 	struct block_device	*bdev;		/* dev attached */
 	dev_t			dev;		/* dev attached */
-	dev_t			pkt_dev;	/* our dev */
 	char			name[20];
 	struct packet_settings	settings;
 	struct packet_stats	stats;

_

Signature · 2 lines
Peter Osterlund - [email protected]
http://w1.894.telia.com/~u89404340
#15 lore raw
In reply to Peter Osterlund
Quoted 3 lines · Peter Osterlund

And here is a combined patch consisting of the 7 patches I've posted so far, which should make it easier for those who want to test the driver. Applies to 2.6.7-bk15 and probably -bk16 and -bk17 too.

I tried the version from -mm7 on my opteron box with a dvd+rw medium and 32 bit user tools. The success was rather mixed. Besides the patch below to make it work at all, I noticed that I was not able to rw-mount /dev/dvd after writing a disk through /dev/pktcdvd0. However, I could actually remount,rw /dev/dvd after mounting it read-only.

Also, I believe I hit a race running hdparm while writing to a mounted /dev/pktcdvd0. This resulted in the writing process getting stuck in wait_on_buffer, but I could not reproduce this behaviour later.

Review

Quoted 8 lines
(quoted diff) +7 -0
+static int pkt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
 
+	switch (cmd) {
+	case PACKET_GET_STATS:
+		if (copy_to_user(&arg, &pd->stats, sizeof(struct packet_stats)))
+			return -EFAULT;
+		break;

This clearly doesn't work, since arg is not what copy_to_user expects here. Even if it worked, this particular interface would still be just wrong and should be removed.

Review

Quoted 11 lines
(quoted diff) +11 -0
+#if PACKET_DEBUG
+#define DPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+#if PACKET_DEBUG > 1
+#define VPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args)
+#else
+#define VPRINTK(fmt, args...)
+#endif

These definitions should not be in the pktcdvd header file, since they conflict with similar definitions in other headers. If you want to keep them, they should be moved to the .c file.

Review

Quoted 2 lines
(quoted diff) +2 -0
+#define PACKET_SETUP_DEV	_IOW(PACKET_IOCTL_MAGIC, 1, unsigned int)
+#define PACKET_TEARDOWN_DEV	_IOW(PACKET_IOCTL_MAGIC, 2, unsigned int)

These are actually incorrect definitions since the ioctl argument is not a pointer to unsigned int but instead just an int. However, that's too late to fix without breaking the existing tools.

	Arnd <><

========
[PATCH] fix packet CD/DVD writing for compat mode

This adds support for 32 bit compatibility mode to the new pktcdvd ioctl interface. The contained changes are:
- Add ULONG_IOCTL() definitions to <linux/compat_ioctl.h> for the setup/teardown ioctls.
- Remove the PACKET_GET_STATS ioctl. It would require a compat handler function to emulate, however it is completely bogus and did not work as designed anyway.
- move conflicting DPRINTK/VPRINTK out of the header file so it can be included from fs/compat_ioctl.c.

drivers/block/pktcdvd.c +12 -5
@@ -52,6 +52,18 @@
 
 #include <asm/uaccess.h>
 
+#if PACKET_DEBUG
+#define DPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+#if PACKET_DEBUG > 1
+#define VPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args)
+#else
+#define VPRINTK(fmt, args...)
+#endif
+
 #define ZONE(sector, pd) (((sector) + (pd)->offset) & ~((pd)->settings.size - 1))
 
 static struct pktcdvd_device *pkt_devs;
@@ -2475,11 +2487,6 @@
 	}
 
 	switch (cmd) {
-	case PACKET_GET_STATS:
-		if (copy_to_user(&arg, &pd->stats, sizeof(struct packet_stats)))
-			return -EFAULT;
-		break;
-
 	case PACKET_SETUP_DEV:
 		if (pd->dev) {
 			printk("pktcdvd: dev already setup\n");

===== fs/compat_ioctl.c 1.37 vs edited =====

fs/compat_ioctl.c +1 -0
@@ -68,6 +68,7 @@
 #include <linux/i2c.h>
 #include <linux/i2c-dev.h>
 #include <linux/wireless.h>
+#include <linux/pktcdvd.h>
 
 #include <net/sock.h>          /* siocdevprivate_ioctl */
 #include <net/bluetooth/bluetooth.h>
include/linux/compat_ioctl.h +3 -0
@@ -382,6 +382,9 @@
 COMPATIBLE_IOCTL(DVD_READ_STRUCT)
 COMPATIBLE_IOCTL(DVD_WRITE_STRUCT)
 COMPATIBLE_IOCTL(DVD_AUTH)
+/* pktcdvd */
+ULONG_IOCTL(PACKET_SETUP_DEV)
+ULONG_IOCTL(PACKET_TEARDOWN_DEV)
 /* Big L */
 ULONG_IOCTL(LOOP_SET_FD)
 COMPATIBLE_IOCTL(LOOP_CLR_FD)

===== include/linux/pktcdvd.h 1.1 vs edited =====

include/linux/pktcdvd.h +12 -25
@@ -40,18 +40,6 @@
  * No user-servicable parts beyond this point ->
  */
 
-#if PACKET_DEBUG
-#define DPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args)
-#else
-#define DPRINTK(fmt, args...)
-#endif
-
-#if PACKET_DEBUG > 1
-#define VPRINTK(fmt, args...) printk(KERN_NOTICE fmt, ##args)
-#else
-#define VPRINTK(fmt, args...)
-#endif
-
 /*
  * device types
  */
@@ -98,22 +86,9 @@
 #undef PACKET_USE_LS
 
 /*
- * Very crude stats for now
- */
-struct packet_stats
-{
-	unsigned long		pkt_started;
-	unsigned long		pkt_ended;
-	unsigned long		secs_w;
-	unsigned long		secs_rg;
-	unsigned long		secs_r;
-};
-
-/*
  * packet ioctls
  */
 #define PACKET_IOCTL_MAGIC	('X')
-#define PACKET_GET_STATS	_IOR(PACKET_IOCTL_MAGIC, 0, struct packet_stats)
 #define PACKET_SETUP_DEV	_IOW(PACKET_IOCTL_MAGIC, 1, unsigned int)
 #define PACKET_TEARDOWN_DEV	_IOW(PACKET_IOCTL_MAGIC, 2, unsigned int)
 
@@ -131,6 +106,18 @@
 	__u8			write_type;
 	__u8			track_mode;
 	__u8			block_mode;
+};
+
+/*
+ * Very crude stats for now
+ */
+struct packet_stats
+{
+	unsigned long		pkt_started;
+	unsigned long		pkt_ended;
+	unsigned long		secs_w;
+	unsigned long		secs_rg;
+	unsigned long		secs_r;
 };
 
 struct packet_cdrw
#16 lore raw
Quoted 3 lines · Arnd Bergmann

These are actually incorrect definitions since the ioctl argument is not a pointer to unsigned int but instead just an int. However, that's too late to fix without breaking the existing tools.

The tools need to change anyway to get away from the broken behaviour to issue in ioctl on the actual block device to bind it..

#17 lore raw
Quoted 7 lines · Christoph Hellwig
Quoted 3 lines · Arnd Bergmann

These are actually incorrect definitions since the ioctl argument is not a pointer to unsigned int but instead just an int. However, that's too late to fix without breaking the existing tools.

The tools need to change anyway to get away from the broken behaviour to issue in ioctl on the actual block device to bind it..

OK, I'll create a patch that gets rid of the ioctl interface and uses an auxiliary character device instead to control device bindings.

Signature · 2 lines
Peter Osterlund - [email protected]
http://w1.894.telia.com/~u89404340
#18 lore raw
Quoted 2 lines · Peter Osterlund

OK, I'll create a patch that gets rid of the ioctl interface and uses an auxiliary character device instead to control device bindings.

I don't think that's a good idea either. It will solve the udev problem, but it essentially means you're sneaking in system calls through the back door here.

Maybe we can decide on one of the following approaches (or yet another one):

- Do the ioctl call on the cdrom device through cdrom_ioctl() instead of the pktcdvd driver. You need to hook into the cdrom driver subsystem, but leave everything else as it is today.
- Register all cdrom devices with pktcdvd as soon as they are detected. This needs some more changes to the cdrom subsystem, but gets rid of the need for the pktsetup interface because you can do the setup stuff at open time.
- Merge pktcdvd completely into the cdrom subsystem so the existing cdrom device node passes everything down to pktcdvd if an RW medium is mounted writable.

	Arnd <><
#19 lore raw
Quoted 3 lines

- Merge pktcdvd completely into the cdrom subsystem so the existing cdrom device node passes everything down to pktcdvd if an RW medium is mounted writable.

Well, we had that already earlier on. I'd prefer that and it seems Jens, too. But it's a lot of work, and definitly not 2.6 material.

#20 lore raw
Quoted 6 lines · Christoph Hellwig
Quoted 3 lines

- Merge pktcdvd completely into the cdrom subsystem so the existing cdrom device node passes everything down to pktcdvd if an RW medium is mounted writable.

Well, we had that already earlier on. I'd prefer that and it seems Jens, too. But it's a lot of work, and definitly not 2.6 material.

Not really. I'd prefer adding the large block size support to UDF, so the ide-cd/sr write setup would just automatically work without any changes in that path. The needed changes would then just be in setup and closing of the device.

Signature · 1 line
Jens Axboe
#21 lore raw

RFC PATCH 1/1 Control pktcdvd with an auxiliary character device

In reply to Peter Osterlund
Quoted 7 lines · Peter Osterlund
Quoted 5 lines · Christoph Hellwig
Quoted 3 lines · Arnd Bergmann

These are actually incorrect definitions since the ioctl argument is not a pointer to unsigned int but instead just an int. However, that's too late to fix without breaking the existing tools.

Quoted 5 lines
Quoted 2 lines

The tools need to change anyway to get away from the broken behaviour to issue in ioctl on the actual block device to bind it..

OK, I'll create a patch that gets rid of the ioctl interface and uses an auxiliary character device instead to control device bindings.

Here is a patch for 2.6.7-mm7 that does that. The driver creates a misc character device and bind/unbind of the block devices are controlled by ioctl commands on the char device.

This patch needs corresponding changes in the pktsetup user space program. I'll post a patch for pktsetup as a separate message.

The driver no longer uses reserved device numbers. Instead, pktsetup reads /proc/misc to find the pktcdvd char device.

---

 linux-petero/Documentation/cdrom/packet-writing.txt |   18 
 linux-petero/drivers/block/pktcdvd.c                |  588 ++++++++++----------
 linux-petero/include/linux/compat_ioctl.h           |    2 
 linux-petero/include/linux/major.h                  |    2 
 linux-petero/include/linux/pktcdvd.h                |   54 -
 5 files changed, 341 insertions(+), 323 deletions(-)
Documentation/cdrom/packet-writing.txt renamed from Documentation/cdrom/packet-writing.txt~packet-char-dev +9 -11
diff -puN Documentation/cdrom/packet-writing.txt~packet-char-dev Documentation/cdrom/packet-writing.txt
@@ -13,13 +13,11 @@ Getting started quick
   as appropriate):
 	# cdrwtool -d /dev/hdc -q
 
-- Make sure that /dev/pktcdvd0 exists (mknod /dev/pktcdvd0 b 97 0)
-
 - Setup your writer
-	# pktsetup /dev/pktcdvd0 /dev/hdc
+	# pktsetup dev_name /dev/hdc
 
-- Now you can mount /dev/pktcdvd0 and copy files to it. Enjoy!
-	# mount /dev/pktcdvd0 /cdrom -t udf -o rw,noatime
+- Now you can mount /dev/pktcdvd/dev_name and copy files to it. Enjoy!
+	# mount /dev/pktcdvd/dev_name /cdrom -t udf -o rw,noatime
 
 
 Packet writing for DVD-RW media
@@ -33,8 +31,8 @@ overwrite mode, run:
 
 You can then use the disc the same way you would use a CD-RW disc:
 
-	# pktsetup /dev/pktcdvd0 /dev/hdc
-	# mount /dev/pktcdvd0 /cdrom -t udf -o rw,noatime
+	# pktsetup dev_name /dev/hdc
+	# mount /dev/pktcdvd/dev_name /cdrom -t udf -o rw,noatime
 
 
 Packet writing for DVD+RW media
@@ -56,9 +54,9 @@ writes are not 32KB aligned.
 Both problems can be solved by using the pktcdvd driver, which always
 generates aligned writes.
 
-	# pktsetup /dev/pktcdvd0 /dev/hdc
-	# mkudffs /dev/pktcdvd0
-	# mount /dev/pktcdvd0 /cdrom -t udf -o rw,noatime
+	# pktsetup dev_name /dev/hdc
+	# mkudffs /dev/pktcdvd/dev_name
+	# mount /dev/pktcdvd/dev_name /cdrom -t udf -o rw,noatime
 
 
 Notes
@@ -76,7 +74,7 @@ Notes
   device, you can put any filesystem you like on the disc. For
   example, run:
 
-	# /sbin/mke2fs /dev/pktcdvd0
+	# /sbin/mke2fs /dev/pktcdvd/dev_name
 
   to create an ext2 filesystem on the disc.
 
drivers/block/pktcdvd.c renamed from drivers/block/pktcdvd.c~packet-char-dev +303 -285

812 lines not on this page · raw message

include/linux/compat_ioctl.h renamed from include/linux/compat_ioctl.h~packet-char-dev +2 -0
diff -puN include/linux/compat_ioctl.h~packet-char-dev include/linux/compat_ioctl.h
@@ -382,6 +382,8 @@ COMPATIBLE_IOCTL(CDROMREADALL)
 COMPATIBLE_IOCTL(DVD_READ_STRUCT)
 COMPATIBLE_IOCTL(DVD_WRITE_STRUCT)
 COMPATIBLE_IOCTL(DVD_AUTH)
+/* pktcdvd */
+COMPATIBLE_IOCTL(PACKET_CTRL_CMD)
 /* Big L */
 ULONG_IOCTL(LOOP_SET_FD)
 COMPATIBLE_IOCTL(LOOP_CLR_FD)
include/linux/major.h renamed from include/linux/major.h~packet-char-dev +0 -2
diff -puN include/linux/major.h~packet-char-dev include/linux/major.h
@@ -111,8 +111,6 @@
 
 #define MDISK_MAJOR		95
 
-#define PACKET_MAJOR		97
-
 #define UBD_MAJOR		98
 
 #define JSFD_MAJOR		99
include/linux/pktcdvd.h renamed from include/linux/pktcdvd.h~packet-char-dev +27 -27

93 lines not on this page · raw message

_

Signature · 2 lines
Peter Osterlund - [email protected]
http://w1.894.telia.com/~u89404340
#22 lore raw

Re: [RFC][PATCH] Control pktcdvd with an auxiliary character device

Quoted 15 lines · Peter Osterlund
Quoted 7 lines · Peter Osterlund
Quoted 5 lines · Christoph Hellwig
Quoted 3 lines · Arnd Bergmann

These are actually incorrect definitions since the ioctl argument is not a pointer to unsigned int but instead just an int. However, that's too late to fix without breaking the existing tools.

Quoted 5 lines
Quoted 2 lines

The tools need to change anyway to get away from the broken behaviour to issue in ioctl on the actual block device to bind it..

OK, I'll create a patch that gets rid of the ioctl interface and uses an auxiliary character device instead to control device bindings.

Quoted 6 lines

Here is a patch for 2.6.7-mm7 that does that. The driver creates a misc character device and bind/unbind of the block devices are controlled by ioctl commands on the char device.

This patch needs corresponding changes in the pktsetup user space program. I'll post a patch for pktsetup as a separate message.

And here is a patch for udftools-1.0.0b3 that updates the pktsetup program to make it able to use the character device for block device setup/teardown.

---

 udftools-1.0.0b3-petero/pktsetup/pktsetup.c |  245 +++++++++++++++++++++++++++-
 1 files changed, 238 insertions(+), 7 deletions(-)
pktsetup/pktsetup.c renamed from pktsetup/pktsetup.c~pktsetup-char-dev +238 -7
diff -puN pktsetup/pktsetup.c~pktsetup-char-dev pktsetup/pktsetup.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 1999,2000	Jens Axboe <[email protected]>
+ * Copyright (c) 2004		Peter Osterlund <[email protected]>
  *
  *   This program is free software; you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
@@ -19,6 +20,7 @@
 #include <stdio.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
+#include <sys/stat.h>
 #include <unistd.h>
 #include <getopt.h>
 #include <bits/types.h>
@@ -33,8 +35,33 @@
 #define PACKET_SETUP_DEV	_IOW('X', 1, unsigned int)
 #define PACKET_TEARDOWN_DEV	_IOW('X', 2, unsigned int)
 #endif
+#ifndef PACKET_CTRL_CMD
+#define PACKET_CTRL_CMD		_IOWR('X', 1, struct pkt_ctrl_command)
+#endif
+
+#define MAJOR(dev)      ((dev & 0xfff00) >> 8)
+#define MINOR(dev)      ((dev & 0xff) | ((dev >> 12) & 0xfff00))
+#define MKDEV(ma,mi)    ((mi & 0xff) | (ma << 8) | ((mi & ~0xff) << 12))
+
+#define MISC_MAJOR		10
+#define CTL_DIR "/dev/pktcdvd"
+#define CTL_DEV "control"
+
+#define PKT_CTRL_CMD_SETUP	0
+#define PKT_CTRL_CMD_TEARDOWN	1
+#define PKT_CTRL_CMD_STATUS	2
+
+struct pkt_ctrl_command {
+	__u32 command;				/* in: Setup, teardown, status */
+	__u32 dev_index;			/* in/out: Device index */
+	__u32 dev;				/* in/out: Device nr for cdrw device */
+	__u32 pkt_dev;				/* out: Device nr for packet device */
+	__u32 num_devices;			/* out: Largest device index + 1 */
+	__u32 padding;
+};
+
 
-int init_cdrom(int fd)
+static int init_cdrom(int fd)
 {
 	if (ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT) < 0) {
 		perror("drive not ready\n");
@@ -54,7 +81,7 @@ int init_cdrom(int fd)
 	return 0;
 }
 
-void setup_dev(char *pkt_device, char *device, int rem)
+static void setup_dev(char *pkt_device, char *device, int rem)
 {
 	int pkt_fd, dev_fd, cmd;
 
@@ -88,29 +115,233 @@ void setup_dev(char *pkt_device, char *d
 	close(pkt_fd);
 }
 
-int usage(void)
+static int usage(void)
 {
-	printf("pktsetup /dev/pktcdvd0 /dev/cdrom\tsetup device\n");
-	printf("pktsetup -d /dev/pktcdvd0\t\ttear down device\n");
+	printf("For pktcdvd < 0.2.0:\n");
+	printf("  pktsetup /dev/pktcdvd0 /dev/cdrom  setup device\n");
+	printf("  pktsetup -d /dev/pktcdvd0          tear down device\n");
+	printf("For pktcdvd >= 0.2.0:\n");
+	printf("  pktsetup dev_name /dev/cdrom       setup device\n");
+	printf("  pktsetup -d dev_name               tear down device\n");
+	printf("  pktsetup -d major:minor            tear down device\n");
+	printf("  pktsetup -s                        show device mappings\n");
 	return 1;
 }
 
+/*
+ * Find the minor device number for the pktcdvd control device.
+ */
+static int get_misc_minor(void)
+{
+	int minor;
+	char name[64];
+	FILE *f;
+
+	if ((f = fopen("/proc/misc", "r")) == NULL)
+		return -1;
+	while (fscanf(f, " %d %64s", &minor, name) == 2) {
+		if (strcmp(name, "pktcdvd") == 0) {
+			fclose(f);
+			return minor;
+		}
+	}
+	fclose(f);
+	return -1;
+}
+
+static const char *pkt_dev_name(const char *dev)
+{
+	static char buf[128];
+	snprintf(buf, sizeof(buf), "%s/%s", CTL_DIR, dev);
+	return buf;
+}
+
+static void create_ctl_dev(void)
+{
+	int misc_minor;
+	struct stat stat_buf;
+	int dev;
+
+	if ((misc_minor = get_misc_minor()) < 0) {
+		system("/sbin/modprobe pktcdvd");
+		misc_minor = get_misc_minor();
+	}
+	if (misc_minor < 0) {
+		fprintf(stderr, "Can't find pktcdvd character device\n");
+		return;
+	}
+	dev = MKDEV(MISC_MAJOR, misc_minor);
+
+	if ((stat(pkt_dev_name(CTL_DEV), &stat_buf) >= 0) &&
+	    S_ISCHR(stat_buf.st_mode) && (stat_buf.st_rdev == dev))
+		return;			    /* Already set up */
+
+	mkdir(CTL_DIR, 0755);
+	unlink(pkt_dev_name(CTL_DEV));
+	mknod(pkt_dev_name(CTL_DEV), S_IFCHR | 0644, dev);
+}
+
+static int remove_stale_dev_node(int ctl_fd, char *devname)
+{
+	struct stat stat_buf;
+	int i, dev;
+	struct pkt_ctrl_command c;
+
+	if (stat(pkt_dev_name(devname), &stat_buf) < 0)
+		return 0;
+	if (!S_ISBLK(stat_buf.st_mode))
+		return 1;
+	dev = stat_buf.st_rdev;
+	memset(&c, 0, sizeof(struct pkt_ctrl_command));
+	for (i = 0; ; i++) {
+		c.command = PKT_CTRL_CMD_STATUS;
+		c.dev_index = i;
+		if (ioctl(ctl_fd, PACKET_CTRL_CMD, &c) < 0) {
+			perror("ioctl");
+			return 1;
+		}
+		if (i >= c.num_devices)
+			break;
+		if (c.pkt_dev == dev)
+			return 1;	    /* busy */
+	}
+	unlink(pkt_dev_name(devname));
+	return 0;
+}
+
+static void setup_dev_chardev(char *pkt_device, char *device, int rem)
+{
+	struct pkt_ctrl_command c;
+	struct stat stat_buf;
+	int ctl_fd, dev_fd;
+
+	memset(&c, 0, sizeof(struct pkt_ctrl_command));
+
+	create_ctl_dev();
+	if ((ctl_fd = open(pkt_dev_name(CTL_DEV), O_RDONLY)) < 0) {
+		perror("ctl open");
+		return;
+	}
+
+	if (!rem) {
+		if ((dev_fd = open(device, O_RDONLY | O_NONBLOCK)) == -1) {
+			perror("open cd-rom");
+			goto out_close;
+		}
+		if (init_cdrom(dev_fd)) {
+			close(dev_fd);
+			goto out_close;
+		}
+		close(dev_fd);
+
+		if (stat(device, &stat_buf) < 0) {
+			perror("stat cd-rom");
+			goto out_close;
+		}
+		if (!S_ISBLK(stat_buf.st_mode)) {
+			fprintf(stderr, "Not a block device\n");
+			goto out_close;
+		}
+		c.command = PKT_CTRL_CMD_SETUP;
+		c.dev = stat_buf.st_rdev;
+
+		if (remove_stale_dev_node(ctl_fd, pkt_device) != 0) {
+			fprintf(stderr, "Device node '%s' already in use\n", pkt_device);
+			goto out_close;
+		}
+		if (ioctl(ctl_fd, PACKET_CTRL_CMD, &c) < 0) {
+			perror("ioctl");
+			goto out_close;
+		}
+		mknod(pkt_dev_name(pkt_device), S_IFBLK | 0640, c.pkt_dev);
+	} else {
+		int major, minor, remove_node;
+
+		if ((stat(pkt_dev_name(pkt_device), &stat_buf) >= 0) &&
+		    S_ISBLK(stat_buf.st_mode)) {
+			major = MAJOR(stat_buf.st_rdev);
+			minor = MINOR(stat_buf.st_rdev);
+			remove_node = 1;
+		} else if (sscanf(pkt_device, "%d:%d", &major, &minor) == 2) {
+			remove_node = 0;
+		} else {
+			fprintf(stderr, "Can't find major/minor numbers\n");
+			goto out_close;
+		}
+
+		c.command = PKT_CTRL_CMD_TEARDOWN;
+		c.pkt_dev = MKDEV(major, minor);
+		if (ioctl(ctl_fd, PACKET_CTRL_CMD, &c) < 0) {
+			perror("ioctl");
+			goto out_close;
+		}
+		if (remove_node)
+			unlink(pkt_dev_name(pkt_device));
+	}
+
+out_close:
+	close(ctl_fd);
+}
+
+static void show_mappings(void)
+{
+	struct pkt_ctrl_command c;
+	int ctl_fd, i;
+
+	memset(&c, 0, sizeof(struct pkt_ctrl_command));
+
+	create_ctl_dev();
+	if ((ctl_fd = open(pkt_dev_name(CTL_DEV), O_RDONLY)) < 0) {
+		perror("ctl open");
+		return;
+	}
+
+	for (i = 0; ; i++) {
+		c.command = PKT_CTRL_CMD_STATUS;
+		c.dev_index = i;
+		if (ioctl(ctl_fd, PACKET_CTRL_CMD, &c) < 0) {
+			perror("ioctl");
+			goto out_close;
+		}
+		if (i >= c.num_devices)
+			break;
+		if (c.dev) {
+			printf("%2d : %d:%d -> %d:%d\n",
+			       i, MAJOR(c.pkt_dev), MINOR(c.pkt_dev),
+			       MAJOR(c.dev), MINOR(c.dev));
+		}
+	}
+
+out_close:
+	close(ctl_fd);
+}
+
 int main(int argc, char **argv)
 {
 	int rem = 0, c;
+	char *pkt_device;
+	char *device;
 
 	if (argc == 1)
 		return usage();
 
-	while ((c = getopt(argc, argv, "d")) != EOF) {
+	while ((c = getopt(argc, argv, "ds?")) != EOF) {
 		switch (c) {
 			case 'd':
 				rem = 1;
 				break;
+			case 's':
+				show_mappings();
+				return 0;
 			default:
 				return usage();
 		}
 	}
-	setup_dev(argv[optind], argv[optind + 1], rem);
+	pkt_device = argv[optind];
+	device = argv[optind + 1];
+	if (strchr(pkt_device, '/'))
+		setup_dev(pkt_device, device, rem);
+	else
+		setup_dev_chardev(pkt_device, device, rem);
 	return 0;
 }

_

Signature · 2 lines
Peter Osterlund - [email protected]
http://w1.894.telia.com/~u89404340