[xiph-commits] r15183 - in trunk/cdparanoia: . interface
xiphmont at svn.xiph.org
xiphmont at svn.xiph.org
Thu Aug 14 16:24:00 PDT 2008
Author: xiphmont
Date: 2008-08-14 16:23:59 -0700 (Thu, 14 Aug 2008)
New Revision: 15183
Modified:
trunk/cdparanoia/interface/cdda_interface.h
trunk/cdparanoia/interface/common_interface.c
trunk/cdparanoia/interface/cooked_interface.c
trunk/cdparanoia/interface/interface.c
trunk/cdparanoia/interface/low_interface.h
trunk/cdparanoia/interface/scan_devices.c
trunk/cdparanoia/interface/scsi_interface.c
trunk/cdparanoia/main.c
Log:
Commit ongoing cache timing analysis code.
Also modify internal setup flow to allow setspeed to be issued before cdda_open()
Add SCSI version of setspeed.
Modified: trunk/cdparanoia/interface/cdda_interface.h
===================================================================
--- trunk/cdparanoia/interface/cdda_interface.h 2008-08-14 16:51:39 UTC (rev 15182)
+++ trunk/cdparanoia/interface/cdda_interface.h 2008-08-14 23:23:59 UTC (rev 15183)
@@ -198,7 +198,10 @@
402: Track not audio data
403: No audio tracks on disc
404: No medium present
+405: Option not supported by drive
+500: Drive timing analysis read failure
+501: Drive timing analysis indeterminate
*/
#endif
Modified: trunk/cdparanoia/interface/common_interface.c
===================================================================
--- trunk/cdparanoia/interface/common_interface.c 2008-08-14 16:51:39 UTC (rev 15182)
+++ trunk/cdparanoia/interface/common_interface.c 2008-08-14 23:23:59 UTC (rev 15183)
@@ -175,42 +175,50 @@
For that reason, we need to empirically determine cache size used
for reads */
-int cdda_cache_sectors(cdrom_drive *d){
+int analyze_timing_and_cache(cdrom_drive *d){
/* Some assumptions about timing:
- The physically fastest drives are about 50x, and usually only at
- the rim. This has been stable for nearly ten years at this
- point. It's possible to make faster drives using multiple read
- pickups and interleaving, but it doesn't appear anyone cares to.
+ We can't perform cache determination timing based on looking at
+ average transfer times; on slow setups, the speed of a drive
+ reading sectors via PIO will not be reliably distinguishable from
+ the same drive returning data from the cache via pio. We need
+ something even more noticable and reliable: the seek time. A
+ seek will reliably be approximately 1.5 orders of magnitude
+ faster than a sequential sector access or cache hit, and slower
+ systems will also tend to have slower seeks. It is unlikely we'd
+ ever see a seek latency of under ~10ms given the synchronization
+ requirements of a CD and the maximum possible rotational
+ velocity.
- The slowest interface we're likely to see is UDMA40, which would
- probably be able to maintain 100x in practice to a chain with a
- single device on an otherwise unloaded machine. This is a bit
- too close for comfort to rely on simple timing thresholding,
- especially as the kernel is going to be inserting its own
- unpredictable timing latencies.
+ Further complicating things, we have to watch the data collection
+ carefully as we're not always going to be on an unloaded system,
+ and we even have to guard against other apps accessing the drive
+ (something that should never happen on purpose, but could happen
+ by accident). As we know in our testing when seeks should never
+ occur, a sudden seek-sized latency popping up in the middle of a
+ collection is an indication that collection is possibly invalid.
- The bus itself adds a timing overhead; the SATA 150 machines at my disposal appear to fairly universally insert a roughly .06ms/sector (~200x) transfer latency. PATA UDMA 133 appears to be ~ .1 ms/sector.
+ A second cause of 'spurious latency' would be media damage; if
+ we're consistently hitting latency on the same sector during
+ initial collection, may need to move past it. */
-It's possible to make
- drives faster (multiread, etc), but actual bus throughput at the
- moment only abounts to 100x-200x,
-
- /* let's assume the (physically) fastest drives are 60x; this is true in practice, and drives that fast are usually only that fast out at the rim */
- float ms_per_sector = 1./75./100.*1000;
- int i;
- int lo = 75;
- int current = lo;
- int max = 75*256;
+ int i,ret;
int firstsector=-1;
int lastsector=-1;
int firsttest=-1;
int lasttest=-1;
- int under=1;
+ char buffer[80];
+ int max_retries=20;
+ float median;
+ int offset;
- cdmessage(d,"\nChecking CDROM drive cache behavior...\n");
+ /* set up a default pessimal take on drive behavior */
+ d->private->cache_backseekflush=0;
+ d->private->cache_sectors=1200;
+ cdmessage(d,"\nChecking drive timing behavior...");
+
/* find the longest stretch of available audio data */
for(i=0;i<d->tracks;i++){
@@ -229,28 +237,156 @@
}
if(firstsector==-1){
- cdmessage(d,"\n\tNo audio on disc; Cannot determine audio caching behavior.\n");
+ cdmessage(d,"\n\tNo audio on disc; Cannot determine timing behavior...");
return -1;
}
- while(current <= max && under){
- int offset = (lastsector - firstsector - current)/2;
+ /* initial timing data collection of 100 sequential sectors; we need a median, verify an initial seek */
+ {
+ int x;
+ int current=100;
+ int histogram[10000];
+ int latency[current];
+ int retry;
+ offset = (lastsector - firstsector - current)*2/3 + firstsector;
+
+ for(retry=0;retry<max_retries;retry++){
+ int acc=0;
+ int prev=0;
+
+ if(retry){
+ offset-=current+1;
+ offset-=offset/32;
+ }
+ if(offset<firstsector)break;
+
+ memset(histogram,0,sizeof(histogram));
+ if((ret=d->read_audio(d,NULL,offset+current+1,1))<0){
+ /* media error! grr! retry elsewhere */
+ cdmessage(d,"\n\tWARNING: unrecoverable media error while performing test"
+ "\n\treads; picking new location and trying again.");
+ continue;
+ }
+ for(i=0;i<current;i++){
+ if(d->read_audio(d,NULL,offset+i,1)<0){
+ /* media error! grr! retry elsewhere */
+ cdmessage(d,"\n\tWARNING: unrecoverable media error while performing test"
+ "\n\treads; picking new location and trying again.");
+ break;
+ }
+ x = d->private->last_milliseconds;
+ if(x>9999)x=9999;
+ if(x<0)x=0;
+ histogram[x]++;
+ latency[i]=x;
+ }
+ if(i<current){
+ offset-=current+1;
+ continue;
+ }
+
+ for(i=0;i<10000;i++){
+ prev=acc;
+ acc+=histogram[i];
+ if(acc>current/2) break;
+ }
+
+ median = (i*(acc-prev) + (i-1)*prev)/(float)acc;
+
+ snprintf(buffer,80,"\n\tsmall seek latency (%d sectors): %d ms",current,latency[0]);
+ cdmessage(d,buffer);
+ snprintf(buffer,80,"\n\tmedian read latency per sector: %.1f ms",median);
+ cdmessage(d,buffer);
+
+ /* verify slow spinup did not compromise median */
+ for(i=1;i<current;i++)
+ if(latency[i]>latency[i-1] || latency[i]<=(median+1.))break;
+ if(i>5){
+ cdmessage(d,"\n\tDrive appears to spin up slowly... retrying...");
+ offset-=current+1;
+ continue;
+ }
+
+ /* verify against spurious latency; any additional 5x blocks that
+ are not continuous with read start */
+ acc=0;
+ if(median<.6)median=.6;
+ for(i=5;i<current;i++)
+ if(latency[i]>median*10)acc++;
+
+ if(acc){
+ cderror(d,"\n\tWARNING: Read timing displayed bursts of unexpected"
+ "\n\tlatency; retrying for a clean read.\n");
+ continue;
+ }
+
+ break;
+ }
+
+ if(offset<firstsector){
+ cderror(d,"\n500: Unable to find sufficiently large area of"
+ "\n\tgood media to perform timing tests. Aborting.\n");
+ return -500;
+ }
+ if(retry==max_retries){
+ cderror(d,"\n500: Too many retries; aborting analysis.\n");
+ return -500;
+ }
+ }
+
+ /* look to see if drive is caching at all; read first sector N
+ times, if any reads are near or under the median latency, we're
+ caching */
+ {
+ for(i=0;i<max_retries;i++){
+ if(d->read_audio(d,NULL,offset,1)==1){
+ if(d->private->last_milliseconds<median*10) break;
+
+ }else{
+ /* error handling */
+ }
+ }
+
+ if(i<max_retries){
+ cdmessage(d,"\n\tCaching test result: DRIVE IS CACHING (bad)\n");
+ }else{
+ cdmessage(d,"\n\tCaching test result: Drive is not caching (good)\n");
+ d->private->cache_sectors=0;
+ return 0;
+ }
+ }
+
+
+
+
+
+
+
+ /* bisection search on cache size */
+
+ int lo=10;
+ int hi=15000;
+ int current=lo;
+ int under=1;
+ float ms_per_sector = 1./75.*1000./100.;
+ d->nsectors=1;
+ while(current <= hi && under){
+ int offset = (lastsector - firstsector - current)/2+firstsector;
int i,j;
under=0;
{
char buffer[80];
- snprintf(buffer,80,"\tTesting reads for caching (%d sectors):\n\t",current);
+ snprintf(buffer,80,"\n\tTesting reads for caching (%d sectors):\n\t",current);
cdmessage(d,buffer);
}
for(i=0;i<10;i++){
int sofar=0;
int fulltime=0;
- offset--;
+
while(sofar<current){
- for(j=0;;j++){
-
+ for(j=0;;j++){
int readsectors = d->read_audio(d,NULL,offset+sofar,current-sofar);
if(readsectors<=0){
if(j==2){
@@ -259,7 +395,6 @@
return(-1);
}
}else{
- sofar+=readsectors;
if(d->private->last_milliseconds==-1){
if(j==2){
d->enable_cdda(d,0);
@@ -268,35 +403,29 @@
}
}else{
- fprintf(stderr,">%d:%dms ",readsectors, d->private->last_milliseconds);
- fulltime += d->private->last_milliseconds;
+ if(sofar==0){
+ fprintf(stderr,">%d:%dms ",readsectors, d->private->last_milliseconds);
+ fulltime = d->private->last_milliseconds;
+ }
+ sofar+=readsectors;
break;
}
}
}
}
- {
- char buffer[80];
- snprintf(buffer,80," %d:%fms/sec",i,(float)fulltime/current);
- cdmessage(d,buffer);
- }
- if((float)fulltime/current < ms_per_sector) under=1;
+ if(fulltime < median*10) under=1;
}
cdmessage(d,"\n");
current*=2;
}
-#if 0
- if(current > max){
- cdmessage(d,"\nGiving up; drive cache is too large to defeat using overflow.\n");
- cdmessage(d,"\n(Drives should not cache redbook reads, this drive does anyway."
- "\n Worse, the cache is too large to have any hope of defeating."
- "\n Cdparanoia has no chance of catching errors from this drive.\n");
- return INT_MAX;
- }
-#endif
+
+
+
+ /* XXXXXX IN PROGRESS */
+ cdmessage(d,"\n");
return 0;
}
Modified: trunk/cdparanoia/interface/cooked_interface.c
===================================================================
--- trunk/cdparanoia/interface/cooked_interface.c 2008-08-14 16:51:39 UTC (rev 15182)
+++ trunk/cdparanoia/interface/cooked_interface.c 2008-08-14 23:23:59 UTC (rev 15183)
@@ -79,7 +79,6 @@
return 0;
}
-
/* read 'SectorBurst' adjacent sectors of audio sectors
* to Buffer '*p' beginning at sector 'lSector'
*/
@@ -218,6 +217,11 @@
}
}
+
+int cooked_preinit_drive(cdrom_drive *d){
+ d->set_speed = cooked_setspeed;
+}
+
/* set function pointers to use the ioctl routines */
int cooked_init_drive (cdrom_drive *d){
int ret;
@@ -274,7 +278,6 @@
}
d->enable_cdda = Dummy;
d->read_audio = cooked_read;
- d->set_speed = cooked_setspeed;
d->read_toc = cooked_readtoc;
ret=d->tracks=d->read_toc(d);
if(d->tracks<1)
Modified: trunk/cdparanoia/interface/interface.c
===================================================================
--- trunk/cdparanoia/interface/interface.c 2008-08-14 16:51:39 UTC (rev 15182)
+++ trunk/cdparanoia/interface/interface.c 2008-08-14 23:23:59 UTC (rev 15183)
@@ -90,12 +90,17 @@
/* d->select_speed(d,d->maxspeed); most drives are full speed by default */
if(d->bigendianp==-1)d->bigendianp=data_bigendianp(d);
+ analyze_timing_and_cache(d);
return(0);
}
int cdda_speed_set(cdrom_drive *d, int speed)
{
- return d->set_speed ? d->set_speed(d, speed) : 0;
+ if(d->set_speed)
+ if(!d->set_speed(d,speed)) return 0;
+
+ cderror(d,"405: Option not supported by drive\n");
+ return -405;
}
long cdda_read(cdrom_drive *d, void *buffer, long beginsector, long sectors){
Modified: trunk/cdparanoia/interface/low_interface.h
===================================================================
--- trunk/cdparanoia/interface/low_interface.h 2008-08-14 16:51:39 UTC (rev 15182)
+++ trunk/cdparanoia/interface/low_interface.h 2008-08-14 23:23:59 UTC (rev 15183)
@@ -101,6 +101,8 @@
unsigned char *sg_buffer; /* points into sg_hd */
int last_milliseconds;
+ int cache_backseekflush;
+ int cache_sectors;
};
#define MAX_RETRIES 8
@@ -108,8 +110,10 @@
#define MIN_BIG_BUFF_SIZE 4096
#define SG_OFF sizeof(struct sg_header)
+extern int cooked_preinit_drive (cdrom_drive *d);
extern int cooked_init_drive (cdrom_drive *d);
extern unsigned char *scsi_inquiry (cdrom_drive *d);
+extern int scsi_preinit_drive (cdrom_drive *d);
extern int scsi_init_drive (cdrom_drive *d);
#ifdef CDDA_TEST
extern int test_init_drive (cdrom_drive *d);
Modified: trunk/cdparanoia/interface/scan_devices.c
===================================================================
--- trunk/cdparanoia/interface/scan_devices.c 2008-08-14 16:51:39 UTC (rev 15182)
+++ trunk/cdparanoia/interface/scan_devices.c 2008-08-14 23:23:59 UTC (rev 15183)
@@ -265,7 +265,8 @@
d->nsectors=-1;
d->private=calloc(1,sizeof(*d->private));
idmessage(messagedest,messages,"\t\tCDROM sensed: %s\n",description);
-
+
+ cooked_preinit_drive(d);
return(d);
}
@@ -748,14 +749,14 @@
memcpy(d->inqbytes,p,4);
d->cdda_device_name=copystring(generic_device);
d->ioctl_device_name=copystring(specialized_device);
-
d->drive_model=calloc(36,1);
strscat(d->drive_model,p+8,8);
strscat(d->drive_model,p+16,16);
strscat(d->drive_model,p+32,4);
idmessage(messagedest,messages,"\nCDROM model sensed sensed: %s",d->drive_model);
-
+
+ scsi_preinit_drive(d);
return(d);
cdda_identify_scsi_fail:
Modified: trunk/cdparanoia/interface/scsi_interface.c
===================================================================
--- trunk/cdparanoia/interface/scsi_interface.c 2008-08-14 16:51:39 UTC (rev 15182)
+++ trunk/cdparanoia/interface/scsi_interface.c 2008-08-14 23:23:59 UTC (rev 15183)
@@ -611,46 +611,6 @@
return(0);
}
-static int set_read_ahead (cdrom_drive *d, int start, int end){
- int err;
- unsigned char sense[SG_MAX_SENSE];
- unsigned char cmd[12]={0xA7, /* SET READ AHEAD */
- 0x00, /* reserved */
- 0x00, /* lba */
- 0x00, /* lba */
- 0x00, /* lba */
- 0x00, /* lba */
- 0x00, /* lba */
- 0x00, /* lba */
- 0x00, /* lba */
- 0x00, /* lba */
- 0, /* reserved */
- 0}; /* control */
-
- cmd[2] = (start>>24)&0xff;
- cmd[3] = (start>>16)&0xff;
- cmd[4] = (start>>8)&0xff;
- cmd[5] = (start)&0xff;
-
- cmd[6] = (end>>24)&0xff;
- cmd[7] = (end>>16)&0xff;
- cmd[8] = (end>>8)&0xff;
- cmd[9] = (end)&0xff;
-
- if (err=handle_scsi_cmd (d, cmd, 12, 0, 0, 0, 0, sense)){
- fprintf(stderr,"Unable to disable cache\n");
- fprintf(stderr," Sense key: %x ASC: %x ASCQ: %x\n",
- (int)(sense[2]&0xf),
- (int)(sense[12]),
- (int)(sense[13]));
- fprintf(stderr," Transport error: %s\n",strerror_tr[err]);
- fprintf(stderr," System error: %s\n",strerror(errno));
-
- }
-
- return(0);
-}
-
typedef struct scsi_TOC { /* structure of scsi table of contents (cdrom) */
unsigned char reserved1;
unsigned char bFlags;
@@ -807,6 +767,20 @@
return(tracks);
}
+static int scsi_set_speed (cdrom_drive *d, int speed){
+ int ret;
+ char b[80];
+ unsigned char cmd[12]={0xBB, 0, 0, 0, 0xff, 0xff, 0, 0, 0, 0};
+ unsigned char sense[SG_MAX_SENSE];
+
+ speed=speed*44100*4/1024;
+ cmd[2] = (speed >> 8) & 0xFF;
+ cmd[3] = (speed) & 0xFF;
+ ret=handle_scsi_cmd(d,cmd,12,0,0,0,0,sense);
+
+ return check_sbp_error(ret,sense);
+}
+
/* These do one 'extra' copy in the name of clean code */
static int i_read_28 (cdrom_drive *d, void *p, long begin, long sectors, unsigned char *sense){
@@ -1679,6 +1653,9 @@
return (d->private->sg_buffer);
}
+int scsi_preinit_drive(cdrom_drive *d){
+ d->set_speed = scsi_set_speed;
+}
int scsi_init_drive(cdrom_drive *d){
int ret;
@@ -1723,8 +1700,6 @@
d->read_toc = (!memcmp(d->drive_model, "IMS", 3) && !d->is_atapi) ? scsi_read_toc2 :
scsi_read_toc;
- d->set_speed = NULL;
-
if(!d->is_atapi){
unsigned sector_size= get_orig_sectorsize(d);
Modified: trunk/cdparanoia/main.c
===================================================================
--- trunk/cdparanoia/main.c 2008-08-14 16:51:39 UTC (rev 15182)
+++ trunk/cdparanoia/main.c 2008-08-14 23:23:59 UTC (rev 15183)
@@ -654,7 +654,7 @@
char *force_cdrom_device=NULL;
char *force_generic_device=NULL;
char *force_cooked_device=NULL;
- int force_cdrom_speed=-1;
+ int force_cdrom_speed=0;
int max_retries=20;
char *span=NULL;
int output_type=1; /* 0=raw, 1=wav, 2=aifc */
@@ -937,6 +937,19 @@
}
}
+ /* We want the speed set to apply to cache and timing testing */
+ if(force_cdrom_speed!=0){
+ char buf[80];
+ sprintf(buf,"\nAttempting to set speed to %dx... ",force_cdrom_speed);
+ report(buf);
+ if(cdda_speed_set(d,force_cdrom_speed)){
+ report("\tFAILED.");
+ exit(1);
+ }else{
+ report("\tdrive returned OK.");
+ }
+ }
+
switch(cdda_open(d)){
case -2:case -3:case -4:case -5:
report("\nUnable to open disc. Is there an audio CD in the drive?");
@@ -951,9 +964,6 @@
exit(1);
}
- /* Determine drive caching behavior for cache-busting purposes */
- cdda_cache_sectors(d);
-
/* Dump the TOC */
if(query_only || verbose)display_toc(d);
if(query_only)exit(0);
@@ -978,10 +988,6 @@
d->disc_toc[i].dwStartSector+=toc_offset;
- if(force_cdrom_speed!=-1){
- cdda_speed_set(d,force_cdrom_speed);
- }
-
if(d->nsectors==1){
report("WARNING: The autosensed/selected sectors per read value is\n"
" one sector, making it very unlikely Paranoia can \n"
More information about the commits
mailing list