CDRW packet writing support for 2.6.7-bk13
Subsystems (5)
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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_drCDROM_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
@@ -880,10 +880,10 @@ static void get_capabilities(struct scsicd->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
@@ -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
@@ -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.
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
@@ -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
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 doesmodule_put()afterput_disk().Signed-off-by Peter Osterlund <[email protected]>---
linux-petero/fs/partitions/check.c | 1 + 1 files changed, 1 insertion(+)diff -puN
fs/partitions/check.c~packet-refcntfs/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?
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 doesmodule_put()afterput_disk().Signed-off-by Peter Osterlund <[email protected]>---
linux-petero/fs/partitions/check.c | 1 + 1 files changed, 1 insertion(+)diff -puN
fs/partitions/check.c~packet-refcntfs/partitions/check.cfs/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 internalmodule_get()inkobj_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 inkobject_put(), called fromput_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
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 internalmodule_get()inkobj_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?
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 internalmodule_get()inkobj_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
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()inpktcdvd.c.
Can't you use bdevname(pd->bdev) in there?
PATCH 1/1 Fix open/close races in pktcdvd
In reply to Peter OsterlundThe 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
@@ -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 *inodestruct 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, chareturn 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_deset_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 pktcdvdpkt_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 gendstatic 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 *inodecase 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
@@ -236,7 +236,8 @@ struct pktcdvd_devicechar 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
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()inpktcdvd.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
PATCH 1/1 Fix race in pktcdvd kernel thread handling
In reply to Peter OsterlundRunning "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
@@ -1182,6 +1182,8 @@ static int kcdrwd(void *foobar)siginitsetinv (¤t->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_desprintf (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 pktcdvdprintk ("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
@@ -140,7 +140,7 @@ struct packet_cdrwspinlock_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
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_COMMAND1
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
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
?
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; }
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 writingHuh, 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_COMMAND1Please include
scsi_ioctl.hinstead 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_deviceinstead 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* inq->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_getin ->open,blkdev_putin ->release
I'll work on this tomorrow.
Quoted 4 lines
Review
Quoted 1 line
(quoted diff) +1
-0+ fsync_bdev(bdev);
fs/block_dev.calready does async_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_fileinterface 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.cinfastructure.
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_inodecan'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
@@ -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 (¤t->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 pktcdvdreturn 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_dreturn 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, chastatic 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_depkt_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 pktcdvdremove_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 *inodeup (&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
@@ -138,9 +138,7 @@ struct packet_cdrwstruct 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_deviceatomic_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
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.
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.cJens 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
Quoted 4 lines · Peter Osterlund
For
fsync_bdev(), which you say is not needed anyway. Also for theinvalidate_bdev()call inpkt_remove_dev(). The loop driver is also callinginvalidate_bdev()in itsloop_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..
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_fileinterface 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
@@ -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
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
@@ -1990,7 +1990,6 @@ static void pkt_release_dev(struct pktcdstatic 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 *inodeif (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
@@ -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
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
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..
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
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 <><
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.
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
RFC PATCH 1/1 Control pktcdvd with an auxiliary character device
In reply to Peter OsterlundQuoted 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
@@ -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
@@ -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
@@ -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
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
@@ -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 *dclose (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