updated CDROMREADAUDIO DMA patch
A refresh and retest of this patch, against 2.4.21-pre2. It would be helpful if a few (or a lot of) people could test this, and report
on the result. Otherwise it'll never get anywhere...
Reading audio from IDE CDROMs always uses PIO. This patch teaches the kernel to use DMA for the CDROMREADAUDIO ioctl.
Total time to read a CD using cdparanoia improves by up to 20%. But sometimes there is no change.
Total CPU load decreases greatly. On a 700 MHz VIA C3 with 82C6xx Southbridge the CPU load during the rip falls from 85% to 9%.
On an 850MHz P3 with piix chipset CPU load falls from 76% to 8%.
These loads cannot be observed with top' or ps' - it all happens at interrupt time. I measure with `cyclesoak'.
Recovery from media errors has been tested, works OK.
Recovery from DMA errors has been simulated. It falls back to PIO mode OK.
The cdrom needs to be set into DMA mode (hdparm -d1 /dev/cdrom) for this code to have an effect. I find that
hdparm -c1 -u1 -d1 /dev/cdrom
works nicely.
drivers/cdrom/cdrom.c | 151 +++++++++++++++++++++++++++++++++----------------- drivers/ide/ide-cd.c | 62 +++++++++++++++++++- drivers/ide/ide-cd.h | 1 include/linux/cdrom.h | 9 ++ 4 files changed, 171 insertions(+), 52 deletions(-)
drivers/cdrom/cdrom.c renamed from drivers/cdrom/cdrom.c~ide-akpm +102 -49
@@ -1911,6 +1911,107 @@ static int cdrom_do_cmd(struct cdrom_devreturn ret; } +/* +* CDROM audio read, with DMA support. Added in 2.4.18-pre4, akpm. +* +* Initially, we try to perform multiframe bus-mastering. If the IDE +* layer experiences a DMA error, we fall back to single-frame DMA. +* If the IDE layer again detects a DMA error, we fall back to multiframe +* PIO. +* +* We do not want to disable drive-level DMA at any stage, because +* some devices can perform non-packet DMA quite happily, but appear +* to not be able to perform packet DMA correctly. +* +* If the drive is not using_dma, we never attempt packet DMA. +*/ +static int cdda_read_audio (int cmd, +struct cdrom_device_info *cdi, +struct cdrom_generic_command *cgc, +struct cdrom_read_audio *ra) +{ +int lba; +unsigned frames_todo; +int ret; +void *xferbuf =0 ; +unsigned nr_local_frames; +char *useraddr; + + ret = -EINVAL ; +if (ra->addr_format ==CDROM_MSF ) { + lba =msf_to_lba (ra->addr.msf.minute, + ra->addr.msf.second, + ra->addr.msf.frame); + }else if (ra->addr_format ==CDROM_LBA ) { + lba = ra->addr.lba; + }else { + goto out; + } + +if (lba <0 || ra->nframes <=0 ) + goto out; + +/* +* We can't sensibly support more that 64k because we later +* use a buffer_head to map the temp buffer. And b_count is +* unsigned short. +*/ + nr_local_frames = ra->nframes; +if (nr_local_frames *CD_FRAMESIZE_RAW >32768 ) + nr_local_frames =32768 /CD_FRAMESIZE_RAW ; + +if (cdi->dma_mode ==CDROM_DMA_SINGLE ) + nr_local_frames =1 ; + +do { + xferbuf =kmalloc (CD_FRAMESIZE_RAW * nr_local_frames,GFP_KERNEL ); + }while (!xferbuf && nr_local_frames--); + ret = -ENOMEM ; +if (!xferbuf) + goto out; + + cgc->buffer = xferbuf; + cgc->data_direction =CGC_DATA_READ ; +if (cdi->dma_mode !=CDROM_DMA_NONE ) + cgc->do_dma =1 ; + frames_todo = ra->nframes; + useraddr = ra->buf; +retry: +while (frames_todo) { +unsigned frames_now =min (frames_todo, nr_local_frames); + + cgc->dma_error =0 ; + ret =cdrom_read_block (cdi, cgc, lba, frames_now,1 ,CD_FRAMESIZE_RAW ); +if (ret) { +/* +* Here we implement DMA size fallback +*/ +if (cgc->dma_error && cdi->dma_mode ==CDROM_DMA_MULTI ) { +printk (KERN_WARNING "CDROM: falling back to " +"single frame DMA\n" ); + cdi->dma_mode =CDROM_DMA_SINGLE ; + nr_local_frames =1 ; + goto retry; + }else if (cgc->dma_error && cdi->dma_mode ==CDROM_DMA_SINGLE ) { +printk (KERN_WARNING "CDROM: disabled DMA\n" ); + cdi->dma_mode =CDROM_DMA_NONE ; + goto retry; + } + goto out; + } + ret = -EFAULT ; +if (copy_to_user (useraddr, cgc->buffer,CD_FRAMESIZE_RAW * frames_now)) + goto out; + useraddr +=CD_FRAMESIZE_RAW * frames_now; + frames_todo -= frames_now; + lba += frames_now; + } + ret =0 ; +out: +kfree (xferbuf); +return ret; +} +static int mmc_ioctl (struct cdrom_device_info *cdi,unsigned int cmd,unsigned long arg) {
@@ -1977,57 +2078,9 @@ static int mmc_ioctl(struct cdrom_device }case CDROMREADAUDIO : {struct cdrom_read_audio ra; -int lba, nr; IOCTL_IN(arg, struct cdrom_read_audio, ra); - -if (ra.addr_format ==CDROM_MSF ) - lba =msf_to_lba (ra.addr.msf.minute, - ra.addr.msf.second, - ra.addr.msf.frame); -else if (ra.addr_format ==CDROM_LBA ) - lba = ra.addr.lba; -else -return -EINVAL ; - -/* FIXME: we need upper bound checking, too!! */ -if (lba <0 || ra.nframes <=0 ) -return -EINVAL ; - -/* -* start with will ra.nframes size, back down if alloc fails -*/ - nr = ra.nframes; -do { - cgc.buffer =kmalloc (CD_FRAMESIZE_RAW * nr,GFP_KERNEL ); -if (cgc.buffer) -break ; - - nr >>=1 ; - }while (nr); - -if (!nr) -return -ENOMEM ; - -if (!access_ok (VERIFY_WRITE , ra.buf, ra.nframes*CD_FRAMESIZE_RAW )) { -kfree (cgc.buffer); -return -EFAULT ; - } - cgc.data_direction =CGC_DATA_READ ; -while (ra.nframes >0 ) { -if (nr > ra.nframes) - nr = ra.nframes; - - ret =cdrom_read_block (cdi, &cgc, lba, nr,1 ,CD_FRAMESIZE_RAW ); -if (ret) -break ; -__copy_to_user (ra.buf, cgc.buffer,CD_FRAMESIZE_RAW *nr); - ra.buf +=CD_FRAMESIZE_RAW * nr; - ra.nframes -= nr; - lba += nr; - } -kfree (cgc.buffer); -return ret; +return cdda_read_audio (cmd, cdi, &cgc, &ra); }case CDROMSUBCHNL : {struct cdrom_subchnl q;
drivers/ide/ide-cd.c renamed from drivers/ide/ide-cd.c~ide-akpm +60 -2
102 lines not on this page · raw message
drivers/ide/ide-cd.h renamed from drivers/ide/ide-cd.h~ide-akpm +1 -0
@@ -111,6 +111,7 @@ struct packet_command {int quiet;int timeout;struct request_sense *sense; +int do_dma;unsigned char c[12 ]; };
include/linux/cdrom.h renamed from include/linux/cdrom.h~ide-akpm +8 -1
@@ -287,6 +287,8 @@ struct cdrom_generic_commandunsigned char data_direction;int quiet;int timeout; +int do_dma;/* Try to use DMA */ +int dma_error;/* A DMA_specific error occurred */ void *reserved[1 ]; };
@@ -743,10 +745,15 @@ struct cdrom_device_info {char name[20 ];/* name of the device type */ /* per-device flags */ __u8 sanyo_slot : 2; /* Sanyo 3 CD changer support */ - __u8 reserved : 6; /* not used yet */ + __u8 dma_mode : 2; /* See below */ + __u8 reserved : 4; /* not used yet */struct cdrom_write_settings write; }; +#define CDROM_DMA_MULTI 0/* Multiframe DMA (default) */ +#define CDROM_DMA_SINGLE 1/* Single frame DMA */ +#define CDROM_DMA_NONE 2/* Multiframe PIO */ +struct cdrom_device_ops {/* routines */ int (*open) (struct cdrom_device_info *,int );
_
Quoted 7 lines · On 2003.01.05 Andrew Morton wrote:
A refresh and retest of this patch, against 2.4.21-pre2. It would be helpful if a few (or a lot of) people could test this, and report
on the result. Otherwise it'll never get anywhere...Reading audio from IDE CDROMs always uses PIO. This patch teaches the kernel to use DMA for the CDROMREADAUDIO ioctl.
cdparanoia oopses on top of latest -aa:
Checking /dev/cdrom for cdrom...
Testing /dev/cdrom for cooked ioctl() interface
CDROM sensed: ATAPI compatible CREATIVE CD5230E
Verifying drive can read CDDA...
Unable to handle kernel NULL pointer dereference at virtual address 00000014
*pde = 00000000
...
Decoded oops:
ksymoops 2.4.8 on i686 2.4.21-pre3-jam1. Options used
-V (default)
-k /proc/ksyms (default)
-l /proc/modules (default)
-o /lib/modules/2.4.21-pre3-jam1/ (default) -m /boot/System.map-2.4.21-pre3-jam1 (default)
Warning: You did not tell me where to find symbol information. I will assume that the log matches the kernel and modules that are running right now and I'll use the default options above for symbol resolution. If the current kernel and/or modules do not match the log, you can get more accurate output by telling me the kernel version and where to find map, modules, ksyms etc. ksymoops -h explains the options.
Unable to handle kernel NULL pointer dereference at virtual address 00000014 *pde = 00000000 Oops: 0000 2.4.21-pre3-jam1 #1 SMP sab ene 11 01:11:42 CET 2003 CPU: 1
EIP: 0010:[<401c06c0>] Not tainted
Using defaults from ksymoops -t elf32-i386 -a i386
EFLAGS: 00010046
eax: 00000001 ebx: 00000000 ecx: 00000001 edx: 40334600
esi: 47a016d0 edi: 47a01730 ebp: 00000004 esp: 419bbe98
ds: 0010 es: 0010 ss: 0018
Process swapper (pid: 0, stackpage=4199000)
Stack: 419cbfc8 402a2a14 00000001 419bbed0 00000002 47a01730 40334800 00000206 00000001 428105d5 47a01730 00000001 40334800 00000930 47a017ec 40334800 00000001 42811b96 40334800 00000001 00000000 419bbf00 00000003 ffffff80 Call Trace: [<428105d5>] [<42811b96>] [<401dd790>] [<401d6546>] [<42811ab0>] [<40109520>] [<40109748>] [<40105340>] [<4010c2a8>] [<40105340>] [<4010536c>] [<401053e2>] [<4011f845>] [<4011fab0>] Code: 8b 43 14 85 c0 78 71 8b 46 2c 89 47 4c c7 46 2c 00 00 00 00
Quoted 4 lines
Quoted 4 lines
EIP;
401c06c0<end_that_request_first+80/150> <=====edx; 40334600 <ide_hwifs+0/2c88>
esp;419bbe98<_end+1681c9c/1cc9e64>
Trace; 428105d5 <[dmi_scan]dmi_ident+1f0f95/410a20> Trace; 42811b96 <[dmi_scan]dmi_ident+1f2556/410a20> Trace; 401dd790 <__ide_dma_test_irq+20/80> Trace; 401d6546 <ide_intr+e6/180>
Trace; 42811ab0 <[dmi_scan]dmi_ident+1f2470/410a20> Trace; 40109520 <handle_IRQ_event+70/b0> Trace; 40109748 <do_IRQ+98/f0>
Trace; 40105340 <default_idle+0/50>
Trace; 4010c2a8 <call_do_IRQ+5/d>
Trace; 40105340 <default_idle+0/50>
Trace; 4010536c <default_idle+2c/50>
Trace; 401053e2 <cpu_idle+32/50>
Trace; 4011f845 <call_console_drivers+65/120> Trace; 4011fab0 <printk+140/180>
Code; 401c06c0 <end_that_request_first+80/150>
00000000 <_EIP>:
Code; 401c06c0 <end_that_request_first+80/150> <=====
0: 8b 43 14 mov 0x14(%ebx),%eax <=====
Code; 401c06c3 <end_that_request_first+83/150>
3: 85 c0 test %eax,%eax
Code; 401c06c5 <end_that_request_first+85/150>
5: 78 71 js 78 <_EIP+0x78>
Code; 401c06c7 <end_that_request_first+87/150>
7: 8b 46 2c mov 0x2c(%esi),%eax
Code; 401c06ca <end_that_request_first+8a/150>
a: 89 47 4c mov %eax,0x4c(%edi)
Code; 401c06cd <end_that_request_first+8d/150>
d: c7 46 2c 00 00 00 00 movl $0x0,0x2c(%esi)
1 warning issued. Results may not be reliable.
Signature · 4 lines
J.A. Magallon <[email protected]> \ Software is like sex: werewolf.able.es \ It's better when it's free Mandrake Linux release 9.1 (Cooker) for i586 Linux 2.4.21-pre3-jam1 (gcc 3.2.1 (Mandrake Linux 9.1 3.2.1-2mdk))
Quoted 10 lines · J.A. Magallon
Quoted 7 lines · On 2003.01.05 Andrew Morton wrote:
A refresh and retest of this patch, against 2.4.21-pre2. It would be helpful if a few (or a lot of) people could test this, and report
on the result. Otherwise it'll never get anywhere...Reading audio from IDE CDROMs always uses PIO. This patch teaches the kernel to use DMA for the CDROMREADAUDIO ioctl.
Quoted 7 lines
cdparanoia oopses on top of latest -aa:
Checking
/dev/cdromfor cdrom...
Testing/dev/cdromfor cookedioctl()interface
CDROM sensed: ATAPI compatible CREATIVE CD5230E
Verifying drive can read CDDA...Unable to handle kernel NULL pointer dereference at virtual address 00000014
Yup, we seem to have a bit of a problem there. It works fine on the five machines which I tried it on.
Oh well, thanks for testing. It's unlikely that I will do any further work on this.