|
Packit |
cb6d3d |
/*
|
|
Packit |
cb6d3d |
Copyright (C) 2014 Robert Kausch <robert.kausch@freac.org>
|
|
Packit |
cb6d3d |
Copyright (C) 2008 Monty <monty@xiph.org>
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
This program is free software: you can redistribute it and/or modify
|
|
Packit |
cb6d3d |
it under the terms of the GNU General Public License as published by
|
|
Packit |
cb6d3d |
the Free Software Foundation, either version 3 of the License, or
|
|
Packit |
cb6d3d |
(at your option) any later version.
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
This program is distributed in the hope that it will be useful,
|
|
Packit |
cb6d3d |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
cb6d3d |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
cb6d3d |
GNU General Public License for more details.
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
You should have received a copy of the GNU General Public License
|
|
Packit |
cb6d3d |
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
Packit |
cb6d3d |
*/
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* we can ask most drives what their various caches' sizes are, but no
|
|
Packit |
cb6d3d |
drive will tell if it caches redbook data. None should, many do,
|
|
Packit |
cb6d3d |
and there's no way in (eg) MMC/ATAPI to tell a cdrom drive not to
|
|
Packit |
cb6d3d |
cache when accessing audio. SCSI drives have a FUA facility, but
|
|
Packit |
cb6d3d |
it's not clear how many ignore it. MMC does specify some cache
|
|
Packit |
cb6d3d |
side effect as part of SET READ AHEAD, but it's not clear we can
|
|
Packit |
cb6d3d |
rely on them. For that reason, we need to empirically determine
|
|
Packit |
cb6d3d |
cache size and strategy used for reads. */
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
#include <unistd.h>
|
|
Packit |
cb6d3d |
#include <string.h>
|
|
Packit |
cb6d3d |
#include <stdio.h>
|
|
Packit |
cb6d3d |
#include <math.h>
|
|
Packit |
cb6d3d |
#include <cdio/paranoia/cdda.h>
|
|
Packit |
cb6d3d |
#include <cdio/paranoia/paranoia.h>
|
|
Packit |
cb6d3d |
#include <cdio/paranoia/version.h>
|
|
Packit |
cb6d3d |
#include "cachetest.h"
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* not strictly just seeks, but also recapture and read resume when
|
|
Packit |
cb6d3d |
reading/readahead is suspended and idling */
|
|
Packit |
cb6d3d |
#define MIN_SEEK_MS 6
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
#define reportC(...) {if(progress){fprintf(progress, __VA_ARGS__);} \
|
|
Packit |
cb6d3d |
if(log){fprintf(log, __VA_ARGS__);}}
|
|
Packit |
cb6d3d |
#define printC(...) {if(progress){fprintf(progress, __VA_ARGS__);}}
|
|
Packit |
cb6d3d |
#define logC(...) {if(log){fprintf(log, __VA_ARGS__);}}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
static int time_drive(cdrom_drive_t *d, FILE *progress, FILE *log, int lba, int len, int initial_seek){
|
|
Packit |
cb6d3d |
int i,x;
|
|
Packit |
cb6d3d |
int latency=0;
|
|
Packit |
cb6d3d |
double sum=0;
|
|
Packit |
cb6d3d |
double sumsq=0;
|
|
Packit |
cb6d3d |
int sofar;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
logC("\n");
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
for(i=0,sofar=0;sofar
|
|
Packit |
cb6d3d |
int toread = (i==0 && initial_seek?1:len-sofar);
|
|
Packit |
cb6d3d |
int ret;
|
|
Packit |
cb6d3d |
/* first read should also trigger a short seek; one sector so seek duration dominates */
|
|
Packit |
cb6d3d |
if((ret=cdio_cddap_read_timed(d,NULL,lba+sofar,toread,&x))<=0){
|
|
Packit |
cb6d3d |
/* media error! grr! retry elsewhere */
|
|
Packit |
cb6d3d |
if(ret==-404)return -404;
|
|
Packit |
cb6d3d |
return -1;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
if(x>9999)x=9999;
|
|
Packit |
cb6d3d |
if(x<0)x=0;
|
|
Packit |
cb6d3d |
logC("%d:%d:%d ",lba+sofar,ret,x);
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
sofar+=ret;
|
|
Packit |
cb6d3d |
if(i || !initial_seek){
|
|
Packit |
cb6d3d |
sum+=x;
|
|
Packit |
cb6d3d |
sumsq+= x*x /(float)ret;
|
|
Packit |
cb6d3d |
}else
|
|
Packit |
cb6d3d |
latency=x;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* we count even the upper outliers because the drive is almost
|
|
Packit |
cb6d3d |
certainly reading ahead and that will work itself out as we keep
|
|
Packit |
cb6d3d |
reading to catch up. Besides-- the tests would rather see too
|
|
Packit |
cb6d3d |
slow a timing than too fast; the timing data is used as an
|
|
Packit |
cb6d3d |
optimization when sleeping. */
|
|
Packit |
cb6d3d |
{
|
|
Packit |
cb6d3d |
double mean = sum/(float)(len-1);
|
|
Packit |
cb6d3d |
double stddev = sqrt( (sumsq/(float)(len-1) - mean*mean));
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
if(initial_seek){
|
|
Packit |
cb6d3d |
printC("%4dms seek, %.2fms/sec read [%.1fx]",latency,mean,1000./75./mean);
|
|
Packit |
cb6d3d |
logC("\n\tInitial seek latency (%d sectors): %dms",len,latency);
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
logC("\n\tAverage read latency: %.2fms/sector (raw speed: %.1fx)",mean,1000./75./mean);
|
|
Packit |
cb6d3d |
logC("\n\tRead latency standard deviation: %.2fms/sector",stddev);
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
return sum;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
static float retime_drive(cdrom_drive_t *d, FILE *progress, FILE *log, int lba, int readahead, float oldmean){
|
|
Packit |
cb6d3d |
int sectors = 2000;
|
|
Packit |
cb6d3d |
int total;
|
|
Packit |
cb6d3d |
float newmean;
|
|
Packit |
cb6d3d |
if(sectors*oldmean > 5000) sectors=5000/oldmean;
|
|
Packit |
cb6d3d |
readahead*=10;
|
|
Packit |
cb6d3d |
readahead/=9;
|
|
Packit |
cb6d3d |
if(readahead>sectors)sectors=readahead;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
printC("\bo");
|
|
Packit |
cb6d3d |
logC("\n\tRetiming drive... ");
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
total = time_drive(d,NULL,log,lba,sectors,1);
|
|
Packit |
cb6d3d |
newmean = total/(float)sectors;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
logC("\n\tOld mean=%.2fms/sec, New mean=%.2fms/sec\n",oldmean,newmean);
|
|
Packit |
cb6d3d |
printC("\b");
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
if(newmean>oldmean)return newmean;
|
|
Packit |
cb6d3d |
return oldmean;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
int analyze_cache(cdrom_drive_t *d, FILE *progress, FILE *log, int speed){
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* Some assumptions about timing:
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
We can't perform cache determination timing based on looking at
|
|
Packit |
cb6d3d |
average transfer times; on slow setups, the speed of a drive
|
|
Packit |
cb6d3d |
reading sectors via PIO will not be reliably distinguishable from
|
|
Packit |
cb6d3d |
the same drive returning data from the cache via pio. We need
|
|
Packit |
cb6d3d |
something even more noticable and reliable: the seek time. It is
|
|
Packit |
cb6d3d |
unlikely we'd ever see a seek latency of under ~10ms given the
|
|
Packit |
cb6d3d |
synchronization requirements of a CD and the maximum possible
|
|
Packit |
cb6d3d |
rotational velocity. A cache hit would always be faster, even
|
|
Packit |
cb6d3d |
with PIO.
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
Further complicating things, we have to watch the data collection
|
|
Packit |
cb6d3d |
carefully as we're not always going to be on an unloaded system,
|
|
Packit |
cb6d3d |
and we even have to guard against other apps accessing the drive
|
|
Packit |
cb6d3d |
(something that should never happen on purpose, but could happen
|
|
Packit |
cb6d3d |
by accident). As we know in our testing when seeks should never
|
|
Packit |
cb6d3d |
occur, a sudden seek-sized latency popping up in the middle of a
|
|
Packit |
cb6d3d |
collection is an indication that collection is possibly invalid.
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
A second cause of 'spurious latency' would be media damage; if
|
|
Packit |
cb6d3d |
we're consistently hitting latency on the same sector during
|
|
Packit |
cb6d3d |
initial collection, may need to move past it. */
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
int i,j,ret=0,x;
|
|
Packit |
cb6d3d |
int firstsector=-1;
|
|
Packit |
cb6d3d |
int lastsector=-1;
|
|
Packit |
cb6d3d |
int firsttest=-1;
|
|
Packit |
cb6d3d |
int lasttest=-1;
|
|
Packit |
cb6d3d |
int offset;
|
|
Packit |
cb6d3d |
int warn=0;
|
|
Packit |
cb6d3d |
int current=1000;
|
|
Packit |
cb6d3d |
int hi=15000;
|
|
Packit |
cb6d3d |
int cachesize=0;
|
|
Packit |
cb6d3d |
int readahead=0;
|
|
Packit |
cb6d3d |
int rollbehind=0;
|
|
Packit |
cb6d3d |
int cachegran=0;
|
|
Packit |
cb6d3d |
float mspersector=0;
|
|
Packit |
cb6d3d |
if(speed<=0)speed=-1;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
reportC("\n=================== Checking drive cache/timing behavior ===================\n");
|
|
Packit |
cb6d3d |
d->error_retry=0;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* verify the lib and cache analysis match */
|
|
Packit |
cb6d3d |
if(strcmp(LIBCDIO_PARANOIA_VERSION,paranoia_version())){
|
|
Packit |
cb6d3d |
reportC("\nWARNING: cdparanoia application (and thus the cache tests) does not match the"
|
|
Packit |
cb6d3d |
"\ninstalled (or in use) libcdda_paranoia.so library. The final verdict of this"
|
|
Packit |
cb6d3d |
"\ntesting may or may not be accurate for the actual version of the paranoia"
|
|
Packit |
cb6d3d |
"library. Continuing anyway...\n\n");
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* find the longest stretch of available audio data */
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
for(i=0;i<d->tracks;i++){
|
|
Packit |
cb6d3d |
if(cdda_track_audiop(d,i+1)==1){
|
|
Packit |
cb6d3d |
if(firsttest == -1)
|
|
Packit |
cb6d3d |
firsttest=cdda_track_firstsector(d,i+1);
|
|
Packit |
cb6d3d |
lasttest=cdda_track_lastsector(d,i+1);
|
|
Packit |
cb6d3d |
if(lasttest-firsttest > lastsector-firstsector){
|
|
Packit |
cb6d3d |
firstsector=firsttest;
|
|
Packit |
cb6d3d |
lastsector=lasttest;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
firsttest=-1;
|
|
Packit |
cb6d3d |
lasttest=-1;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
if(firstsector==-1){
|
|
Packit |
cb6d3d |
reportC("\n\tNo audio on disc; Cannot determine timing behavior...");
|
|
Packit |
cb6d3d |
return -1;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* Dump some initial timing data to give a little context for human
|
|
Packit |
cb6d3d |
eyes. Take readings ten minutes apart (45000 sectors) and at end of disk. */
|
|
Packit |
cb6d3d |
{
|
|
Packit |
cb6d3d |
int best=0;
|
|
Packit |
cb6d3d |
int bestcount=0;
|
|
Packit |
cb6d3d |
int iterating=0;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
offset = lastsector-firstsector-current-1;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
reportC("\nSeek/read timing:\n");
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
while(offset>=firstsector){
|
|
Packit |
cb6d3d |
int m = offset/4500;
|
|
Packit |
cb6d3d |
int s = (offset-m*4500)/75;
|
|
Packit |
cb6d3d |
int f = offset-m*4500-s*75;
|
|
Packit |
cb6d3d |
int sofar;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
if(iterating){
|
|
Packit |
cb6d3d |
reportC("\n");
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
printC("\r");
|
|
Packit |
cb6d3d |
logC("\n");
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
reportC("\t[%02d:%02d.%02d]: ",m,s,f);
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* initial seek to put at at a small offset past end of upcoming reads */
|
|
Packit |
cb6d3d |
if((ret=cdda_read(d,NULL,offset+current+1,1))<0){
|
|
Packit |
cb6d3d |
/* media error! grr! retry elsewhere */
|
|
Packit |
cb6d3d |
if(ret==-404)return -1;
|
|
Packit |
cb6d3d |
reportC("\n\tWARNING: media error during read; continuing at next offset...");
|
|
Packit |
cb6d3d |
offset = (offset-firstsector+44999)/45000*45000+firstsector;
|
|
Packit |
cb6d3d |
offset-=45000;
|
|
Packit |
cb6d3d |
continue;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
sofar=time_drive(d,progress, log, offset, current, 1);
|
|
Packit |
cb6d3d |
if(offset==firstsector)mspersector = sofar/(float)current;
|
|
Packit |
cb6d3d |
if(sofar==-404)
|
|
Packit |
cb6d3d |
return -1;
|
|
Packit |
cb6d3d |
else if(sofar<0){
|
|
Packit |
cb6d3d |
reportC("\n\tWARNING: media error during read; continuing at next offset...");
|
|
Packit |
cb6d3d |
offset = (offset-firstsector+44999)/45000*45000+firstsector;
|
|
Packit |
cb6d3d |
offset-=45000;
|
|
Packit |
cb6d3d |
continue;
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
if(!iterating){
|
|
Packit |
cb6d3d |
if(best==0 || sofar*1.01
|
|
Packit |
cb6d3d |
best= sofar;
|
|
Packit |
cb6d3d |
bestcount=0;
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
bestcount+=sofar;
|
|
Packit |
cb6d3d |
if(bestcount>sofar && bestcount>4000)
|
|
Packit |
cb6d3d |
iterating=1;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
if(iterating){
|
|
Packit |
cb6d3d |
offset = (offset-firstsector+44999)/45000*45000+firstsector;
|
|
Packit |
cb6d3d |
offset-=45000;
|
|
Packit |
cb6d3d |
printC(" ");
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
offset--;
|
|
Packit |
cb6d3d |
printC(" spinning up... ");
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
reportC("\n\nAnalyzing cache behavior...\n");
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* search on cache size; cache hits are fast, seeks are not, so a
|
|
Packit |
cb6d3d |
linear search through cache hits up to a miss are faster than a
|
|
Packit |
cb6d3d |
bisection */
|
|
Packit |
cb6d3d |
{
|
|
Packit |
cb6d3d |
int under=1;
|
|
Packit |
cb6d3d |
int onex=0;
|
|
Packit |
cb6d3d |
current=0;
|
|
Packit |
cb6d3d |
offset = firstsector+10;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
while(current <= hi && under){
|
|
Packit |
cb6d3d |
int i,j;
|
|
Packit |
cb6d3d |
under=0;
|
|
Packit |
cb6d3d |
current++;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
if(onex){
|
|
Packit |
cb6d3d |
if(speed==-1){
|
|
Packit |
cb6d3d |
logC("\tAttempting to reset read speed to full... ");
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
logC("\tAttempting to reset read speed to %dx... ",speed);
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
if(cdda_speed_set(d,speed)){
|
|
Packit |
cb6d3d |
logC("failed.\n");
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
logC("drive said OK\n");
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
onex=0;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
printC("\r");
|
|
Packit |
cb6d3d |
reportC("\tFast search for approximate cache size... %d sectors ",current-1);
|
|
Packit |
cb6d3d |
logC("\n");
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
for(i=0;i<25 && !under;i++){
|
|
Packit |
cb6d3d |
for(j=0;;j++){
|
|
Packit |
cb6d3d |
int ret1=0,ret2=0;
|
|
Packit |
cb6d3d |
if(i>=15){
|
|
Packit |
cb6d3d |
int sofar=0;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
if(i==15){
|
|
Packit |
cb6d3d |
printC("\r");
|
|
Packit |
cb6d3d |
reportC("\tSlow verify for approximate cache size... %d sectors",current-1);
|
|
Packit |
cb6d3d |
logC("\n");
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
logC("\tAttempting to reduce read speed to 1x... ");
|
|
Packit |
cb6d3d |
if(cdda_speed_set(d,1)){
|
|
Packit |
cb6d3d |
logC("failed.\n");
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
logC("drive said OK\n");
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
onex=1;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
printC(".");
|
|
Packit |
cb6d3d |
logC("\t\t>>> ");
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
while(sofar
|
|
Packit |
cb6d3d |
ret1 = cdda_read_timed(d,NULL,offset+sofar,current-sofar,&x);
|
|
Packit |
cb6d3d |
logC("slow_read=%d:%d:%d ",offset+sofar,ret1,x);
|
|
Packit |
cb6d3d |
if(ret1<=0)break;
|
|
Packit |
cb6d3d |
sofar+=ret1;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
ret1 = cdda_read_timed(d,NULL,offset+current-1,1,&x);
|
|
Packit |
cb6d3d |
logC("\t\t>>> fast_read=%d:%d:%d ",offset+current-1,ret1,x);
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* Some drives are 'easily distracted' when readahead is
|
|
Packit |
cb6d3d |
running ahead of the read cursor, causing accesses of
|
|
Packit |
cb6d3d |
the earliest sectors in the cache to show bursty
|
|
Packit |
cb6d3d |
latency. If there's no seek here after a borderline
|
|
Packit |
cb6d3d |
long read of the earliest sector in the cache, then the
|
|
Packit |
cb6d3d |
cache must not have been dumped yet. */
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
if (ret==1 && i && x
|
|
Packit |
cb6d3d |
under=1;
|
|
Packit |
cb6d3d |
logC("\n");
|
|
Packit |
cb6d3d |
break;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
ret2 = cdda_read_timed(d,NULL,offset,1,&x);
|
|
Packit |
cb6d3d |
logC("seek_read=%d:%d:%d\n",offset,ret2,x);
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
if(ret1<=0 || ret2<=0){
|
|
Packit |
cb6d3d |
offset+=current+100;
|
|
Packit |
cb6d3d |
if(j==10 || offset+current>lastsector){
|
|
Packit |
cb6d3d |
reportC("\n\tToo many read errors while performing drive cache checks;"
|
|
Packit |
cb6d3d |
"\n\t aborting test.\n\n");
|
|
Packit |
cb6d3d |
return(-1);
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
reportC("\n\tRead error while performing drive cache checks;"
|
|
Packit |
cb6d3d |
"\n\t choosing new offset and trying again.\n");
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
if(x==-1){
|
|
Packit |
cb6d3d |
reportC("\n\tTiming error while performing drive cache checks; aborting test.\n");
|
|
Packit |
cb6d3d |
return(-1);
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
if(x
|
|
Packit |
cb6d3d |
under=1;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
break;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
cachesize=current-1;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
printC("\r");
|
|
Packit |
cb6d3d |
if(cachesize==hi){
|
|
Packit |
cb6d3d |
reportC("\tWARNING: Cannot determine drive cache size or behavior! \n");
|
|
Packit |
cb6d3d |
return 1;
|
|
Packit |
cb6d3d |
}else if(cachesize){
|
|
Packit |
cb6d3d |
reportC("\tApproximate random access cache size: %d sector(s) \n",cachesize);
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
reportC("\tDrive does not cache nonlinear access \n");
|
|
Packit |
cb6d3d |
return 0;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* does the readahead cache exceed the maximum Paranoia currently expects? */
|
|
Packit |
cb6d3d |
{
|
|
Packit |
cb6d3d |
cdrom_paranoia *p=paranoia_init(d);
|
|
Packit |
cb6d3d |
if(cachesize > paranoia_cachemodel_size(p,-1)){
|
|
Packit |
cb6d3d |
reportC("\nWARNING: This drive appears to be caching more sectors of\n"
|
|
Packit |
cb6d3d |
" readahead than Paranoia can currently handle!\n");
|
|
Packit |
cb6d3d |
warn=1;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
paranoia_free(p);
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
if(speed==-1){
|
|
Packit |
cb6d3d |
logC("\tAttempting to reset read speed to full... ");
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
logC("\tAttempting to reset read speed to %d... ",speed);
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
if(cdda_speed_set(d,speed)){
|
|
Packit |
cb6d3d |
logC("failed.\n");
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
logC("drive said OK\n");
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* This is similar to the Fast search above, but just in case the
|
|
Packit |
cb6d3d |
cache is being tracked as multiple areas that are treated
|
|
Packit |
cb6d3d |
differently if non-contiguous.... */
|
|
Packit |
cb6d3d |
{
|
|
Packit |
cb6d3d |
int seekoff = cachesize*3;
|
|
Packit |
cb6d3d |
int under=0;
|
|
Packit |
cb6d3d |
reportC("\tVerifying that cache is contiguous...");
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
for(i=0;i<20 && !under;i++){
|
|
Packit |
cb6d3d |
printC(".");
|
|
Packit |
cb6d3d |
for(j=0;;j++){
|
|
Packit |
cb6d3d |
int ret1,ret2;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
if(offset+seekoff>lastsector){
|
|
Packit |
cb6d3d |
reportC("\n\tOut of readable space on CDROM while performing drive checks;"
|
|
Packit |
cb6d3d |
"\n\t aborting test.\n\n");
|
|
Packit |
cb6d3d |
return(-1);
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
ret1 = cdda_read_timed(d,NULL,offset+seekoff,1,&x);
|
|
Packit |
cb6d3d |
logC("\t\t>>> %d:%d:%d ",offset+seekoff,ret1,x);
|
|
Packit |
cb6d3d |
ret2 = cdda_read_timed(d,NULL,offset,1,&x);
|
|
Packit |
cb6d3d |
logC("seek_read:%d:%d:%d\n",offset,ret2,x);
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
if(ret1<=0 || ret2<=0){
|
|
Packit |
cb6d3d |
offset+=cachesize+100;
|
|
Packit |
cb6d3d |
if(j==10){
|
|
Packit |
cb6d3d |
reportC("\n\tToo many read errors while performing drive cache checks;"
|
|
Packit |
cb6d3d |
"\n\t aborting test.\n\n");
|
|
Packit |
cb6d3d |
return(-1);
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
reportC("\n\tRead error while performing drive cache checks;"
|
|
Packit |
cb6d3d |
"\n\t choosing new offset and trying again.\n");
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
if(x==-1){
|
|
Packit |
cb6d3d |
reportC("\n\tTiming error while performing drive cache checks; aborting test.\n");
|
|
Packit |
cb6d3d |
return(-1);
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
if(x
|
|
Packit |
cb6d3d |
break;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
printC("\r");
|
|
Packit |
cb6d3d |
if(under){
|
|
Packit |
cb6d3d |
reportC("\nWARNING: Drive cache does not appear to be contiguous!\n");
|
|
Packit |
cb6d3d |
warn=1;
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
reportC("\tDrive cache tests as contiguous \n");
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* The readahead cache size ascertained above is likely qualified by
|
|
Packit |
cb6d3d |
background 'rollahead'; that is, the drive's readahead process is
|
|
Packit |
cb6d3d |
often working ahead of our actual linear reads, and if reads stop
|
|
Packit |
cb6d3d |
or are interrupted, readahead continues and overflows the cache.
|
|
Packit |
cb6d3d |
It is also the case that the cache size we determined above is
|
|
Packit |
cb6d3d |
slightly too low because readahead is probably always working
|
|
Packit |
cb6d3d |
ahead of reads.
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
Determine the rollahead size a few ways (which may disagree:
|
|
Packit |
cb6d3d |
1) Read number of sectors equal to cache size; pause; read backward until seek
|
|
Packit |
cb6d3d |
2) Read sectors equal to cache-rollahead; verify reading back to beginning does not seek
|
|
Packit |
cb6d3d |
3) Read sectors equal to cache; pause; read ahead until seek delay
|
|
Packit |
cb6d3d |
*/
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
{
|
|
Packit |
cb6d3d |
int lower=0;
|
|
Packit |
cb6d3d |
int gran=64;
|
|
Packit |
cb6d3d |
int it=3;
|
|
Packit |
cb6d3d |
int tests=0;
|
|
Packit |
cb6d3d |
int under=1;
|
|
Packit |
cb6d3d |
readahead=0;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
while(gran>1 || under){
|
|
Packit |
cb6d3d |
tests++;
|
|
Packit |
cb6d3d |
if(tests>8 && gran<64){
|
|
Packit |
cb6d3d |
gran<<=3;
|
|
Packit |
cb6d3d |
tests=0;
|
|
Packit |
cb6d3d |
it=3;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
if(gran && !under){
|
|
Packit |
cb6d3d |
gran>>=3;
|
|
Packit |
cb6d3d |
tests=0;
|
|
Packit |
cb6d3d |
if(gran==1)it=10;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
under=0;
|
|
Packit |
cb6d3d |
readahead=lower+gran;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
printC("\r");
|
|
Packit |
cb6d3d |
logC("\n");
|
|
Packit |
cb6d3d |
reportC("\tTesting background readahead past read cursor... %d",readahead);
|
|
Packit |
cb6d3d |
printC(" \b\b\b\b\b\b\b\b\b\b\b");
|
|
Packit |
cb6d3d |
for (i=0;i
|
|
Packit |
cb6d3d |
int sofar=0, ret=0;
|
|
Packit |
cb6d3d |
logC("\n\t\t%d >>> ",i);
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
while (sofar
|
|
Packit |
cb6d3d |
ret = cdda_read_timed(d, NULL, offset+sofar,
|
|
Packit |
cb6d3d |
cachesize-sofar, &x);
|
|
Packit |
cb6d3d |
if (ret<=0) goto error;
|
|
Packit |
cb6d3d |
logC("%d:%d:%d ", offset+sofar, ret, x);
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* Some drives can lose sync and perform an internal resync,
|
|
Packit |
cb6d3d |
which can also cause readahead to restart. If we see
|
|
Packit |
cb6d3d |
seek-like delays during the initial cahe load, retry the
|
|
Packit |
cb6d3d |
preload. */
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
sofar += ret;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
printC(".");
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* what we'd predict is needed to let the readahead process work. */
|
|
Packit |
cb6d3d |
{
|
|
Packit |
cb6d3d |
int usec=mspersector*(readahead)*(6+i)*200;
|
|
Packit |
cb6d3d |
int max= 13000*2*readahead; /* corresponds to .5x */
|
|
Packit |
cb6d3d |
if(usec>max)usec=max;
|
|
Packit |
cb6d3d |
logC("sleep=%dus ",usec);
|
|
Packit |
cb6d3d |
usleep(usec);
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* seek to offset+cachesize+readahead */
|
|
Packit |
cb6d3d |
ret = cdda_read_timed(d,NULL,offset+cachesize+readahead-1,1,&x);
|
|
Packit |
cb6d3d |
if(ret<=0)break;
|
|
Packit |
cb6d3d |
logC("seek=%d:%d:%d",offset+cachesize+readahead-1,ret,x);
|
|
Packit |
cb6d3d |
if(x
|
|
Packit |
cb6d3d |
under=1;
|
|
Packit |
cb6d3d |
break;
|
|
Packit |
cb6d3d |
}else if(i%3==1){
|
|
Packit |
cb6d3d |
/* retime the drive just to be conservative */
|
|
Packit |
cb6d3d |
mspersector=retime_drive(d, progress, log, offset, readahead, mspersector);
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
if(under)
|
|
Packit |
cb6d3d |
lower=readahead;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
readahead=lower;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
logC("\n");
|
|
Packit |
cb6d3d |
printC("\r");
|
|
Packit |
cb6d3d |
if(readahead==0){
|
|
Packit |
cb6d3d |
reportC("\tDrive does not read ahead past read cursor (very strange) \n");
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
reportC("\tDrive readahead past read cursor: %d sector(s) \n",readahead);
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
reportC("\tTesting cache tail cursor...");
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
while(1){
|
|
Packit |
cb6d3d |
rollbehind=cachesize;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
for(i=0;i<10 && rollbehind;){
|
|
Packit |
cb6d3d |
int sofar=0,ret=0,retry=0;
|
|
Packit |
cb6d3d |
logC("\n\t\t>>> ");
|
|
Packit |
cb6d3d |
printC(".");
|
|
Packit |
cb6d3d |
while(sofar
|
|
Packit |
cb6d3d |
ret = cdda_read_timed(d,NULL,offset+sofar,cachesize-sofar,&x);
|
|
Packit |
cb6d3d |
if(ret<=0)goto error;
|
|
Packit |
cb6d3d |
logC("%d:%d:%d ",offset+sofar,ret,x);
|
|
Packit |
cb6d3d |
sofar+=ret;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* Pause what we'd predict is needed to let the readahead process work. */
|
|
Packit |
cb6d3d |
{
|
|
Packit |
cb6d3d |
int usec=mspersector*readahead*1500;
|
|
Packit |
cb6d3d |
logC("\n\t\tsleeping %d microseconds",usec);
|
|
Packit |
cb6d3d |
usleep(usec);
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* read backwards until we seek */
|
|
Packit |
cb6d3d |
logC("\n\t\t<<< ");
|
|
Packit |
cb6d3d |
sofar=rollbehind;
|
|
Packit |
cb6d3d |
while(sofar>0){
|
|
Packit |
cb6d3d |
sofar--;
|
|
Packit |
cb6d3d |
ret = cdda_read_timed(d,NULL,offset+sofar,1,&x);
|
|
Packit |
cb6d3d |
if(ret<=0)break;
|
|
Packit |
cb6d3d |
logC("%d:%d:%d ",sofar,ret,x);
|
|
Packit |
cb6d3d |
if(x>=MIN_SEEK_MS){
|
|
Packit |
cb6d3d |
if(rollbehind != sofar+1){
|
|
Packit |
cb6d3d |
rollbehind=sofar+1;
|
|
Packit |
cb6d3d |
i=0;
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
i++;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
break;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
if(sofar==0)rollbehind=0;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
error:
|
|
Packit |
cb6d3d |
if(ret<=0){
|
|
Packit |
cb6d3d |
offset+=cachesize;
|
|
Packit |
cb6d3d |
retry++;
|
|
Packit |
cb6d3d |
if(retry>10 || offset+cachesize>lastsector){
|
|
Packit |
cb6d3d |
reportC("\n\tToo many read errors while performing drive cache checks;"
|
|
Packit |
cb6d3d |
"\n\t aborting test.\n\n");
|
|
Packit |
cb6d3d |
return(-1);
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
reportC("\n\tRead error while performing drive cache checks;"
|
|
Packit |
cb6d3d |
"\n\t choosing new offset and trying again.\n");
|
|
Packit |
cb6d3d |
continue;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* verify that the drive timing didn't suddenly change */
|
|
Packit |
cb6d3d |
{
|
|
Packit |
cb6d3d |
float newms=retime_drive(d, progress, log, offset, readahead, mspersector);
|
|
Packit |
cb6d3d |
if(newms > mspersector*1.2){
|
|
Packit |
cb6d3d |
mspersector=newms;
|
|
Packit |
cb6d3d |
printC("\r");
|
|
Packit |
cb6d3d |
reportC("\tDrive timing changed during test; retrying...");
|
|
Packit |
cb6d3d |
continue;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
break;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
logC("\n");
|
|
Packit |
cb6d3d |
printC("\r");
|
|
Packit |
cb6d3d |
if(rollbehind==0){
|
|
Packit |
cb6d3d |
reportC("\tCache tail cursor tied to read cursor \n");
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
reportC("\tCache tail rollbehind: %d sector(s) \n",rollbehind);
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
reportC("\tTesting granularity of cache tail");
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
while(1){
|
|
Packit |
cb6d3d |
cachegran=cachesize+1;
|
|
Packit |
cb6d3d |
for(i=0;i<10 && cachegran;){
|
|
Packit |
cb6d3d |
int sofar=0,ret=0,retry=0;
|
|
Packit |
cb6d3d |
logC("\n\t\t>>> ");
|
|
Packit |
cb6d3d |
printC(".");
|
|
Packit |
cb6d3d |
while(sofar
|
|
Packit |
cb6d3d |
ret = cdda_read_timed(d,NULL,offset+sofar,cachesize-sofar+1,&x);
|
|
Packit |
cb6d3d |
if(ret<=0)goto error2;
|
|
Packit |
cb6d3d |
logC("%d:%d:%d ",offset+sofar,ret,x);
|
|
Packit |
cb6d3d |
sofar+=ret;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* Pause what we'd predict is needed to let the readahead process work. */
|
|
Packit |
cb6d3d |
{
|
|
Packit |
cb6d3d |
int usec=mspersector*readahead*1500;
|
|
Packit |
cb6d3d |
logC("\n\t\tsleeping %d microseconds",usec);
|
|
Packit |
cb6d3d |
usleep(usec);
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* read backwards until we seek */
|
|
Packit |
cb6d3d |
logC("\n\t\t<<< ");
|
|
Packit |
cb6d3d |
sofar=cachegran;
|
|
Packit |
cb6d3d |
while(sofar){
|
|
Packit |
cb6d3d |
sofar--;
|
|
Packit |
cb6d3d |
ret = cdda_read_timed(d,NULL,offset+sofar,1,&x);
|
|
Packit |
cb6d3d |
if(ret<=0)break;
|
|
Packit |
cb6d3d |
logC("%d:%d:%d ",offset+sofar,ret,x);
|
|
Packit |
cb6d3d |
if(x>=MIN_SEEK_MS){
|
|
Packit |
cb6d3d |
if(cachegran == sofar+1){
|
|
Packit |
cb6d3d |
i++;
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
cachegran=sofar+1;
|
|
Packit |
cb6d3d |
i=0;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
break;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
if(sofar==0)cachegran=0;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
error2:
|
|
Packit |
cb6d3d |
if(ret<=0){
|
|
Packit |
cb6d3d |
offset+=cachesize;
|
|
Packit |
cb6d3d |
retry++;
|
|
Packit |
cb6d3d |
if(retry>10 || offset+cachesize>lastsector){
|
|
Packit |
cb6d3d |
reportC("\n\tToo many read errors while performing drive cache checks;"
|
|
Packit |
cb6d3d |
"\n\t aborting test.\n\n");
|
|
Packit |
cb6d3d |
return(-1);
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
reportC("\n\tRead error while performing drive cache checks;"
|
|
Packit |
cb6d3d |
"\n\t choosing new offset and trying again.\n");
|
|
Packit |
cb6d3d |
continue;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* verify that the drive timing didn't suddenly change */
|
|
Packit |
cb6d3d |
{
|
|
Packit |
cb6d3d |
float newms=retime_drive(d, progress, log, offset, readahead, mspersector);
|
|
Packit |
cb6d3d |
if(newms > mspersector*1.2){
|
|
Packit |
cb6d3d |
mspersector=newms;
|
|
Packit |
cb6d3d |
printC("\r");
|
|
Packit |
cb6d3d |
reportC("\tDrive timing changed during test; retrying...");
|
|
Packit |
cb6d3d |
continue;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
break;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
cachegran -= rollbehind;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
logC("\n");
|
|
Packit |
cb6d3d |
printC("\r");
|
|
Packit |
cb6d3d |
reportC("\tCache tail granularity: %d sector(s) \n",cachegran);
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* Verify that a read that begins before the cached readahead dumps
|
|
Packit |
cb6d3d |
the entire readahead cache */
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* This is tricky because we can't simply read a one sector back
|
|
Packit |
cb6d3d |
seek, then rely on timing/seeking of subsequent sectors; the
|
|
Packit |
cb6d3d |
drive may well not seek ahead if reading linearly would be faster
|
|
Packit |
cb6d3d |
(and it often will be), and simply reading ahead after the seek
|
|
Packit |
cb6d3d |
and watching timing will be inaccurate because the drive may roll
|
|
Packit |
cb6d3d |
some readahead into the initial seek/read before returning the
|
|
Packit |
cb6d3d |
first block. */
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* we will need to use the timing of reading from media in one form
|
|
Packit |
cb6d3d |
or another and thus need to guard against slow bus transfer times
|
|
Packit |
cb6d3d |
[eg, no DMA] swamping the actual read time from media. */
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* sample cache access for five realtime seconds. */
|
|
Packit |
cb6d3d |
{
|
|
Packit |
cb6d3d |
float cachems;
|
|
Packit |
cb6d3d |
float readms;
|
|
Packit |
cb6d3d |
int readsize = cachesize-rollbehind-8;
|
|
Packit |
cb6d3d |
int retry=0;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
if(readsize>cachesize-1)readsize=cachesize-1;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
if(readsize<7){
|
|
Packit |
cb6d3d |
reportC("\tCache size (considering rollbehind) too small to test cache speed.\n");
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
reportC("\tTesting cache transfer speed...");
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* cache timing isn't dependent on rotational speed, so get a good
|
|
Packit |
cb6d3d |
read and then just hammer the cache; we will only need to do it once */
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* we need to time the cache using the most conservative
|
|
Packit |
cb6d3d |
possible read pattern; many drives will flush cache on *any*
|
|
Packit |
cb6d3d |
nonlinear access, but not if the read starts from the same
|
|
Packit |
cb6d3d |
point. The original cache size verification algo knows this,
|
|
Packit |
cb6d3d |
and we need to do it the same way here (this the '0' for
|
|
Packit |
cb6d3d |
'initial_seek' on time_drve */
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
while(1){
|
|
Packit |
cb6d3d |
int ret=time_drive(d, NULL, log, offset, readsize, 0);
|
|
Packit |
cb6d3d |
if(ret==-404) return -1;
|
|
Packit |
cb6d3d |
if(ret>0)break;
|
|
Packit |
cb6d3d |
retry++;
|
|
Packit |
cb6d3d |
if(retry==10){
|
|
Packit |
cb6d3d |
reportC("\n\tToo many read errors while performing drive cache checks;"
|
|
Packit |
cb6d3d |
"\n\t aborting test.\n\n");
|
|
Packit |
cb6d3d |
return(-1);
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
reportC("\n\tRead error while performing drive cache checks;"
|
|
Packit |
cb6d3d |
"\n\t choosing new offset and trying again.\n");
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
{
|
|
Packit |
cb6d3d |
int elapsed=0;
|
|
Packit |
cb6d3d |
int sectors=0;
|
|
Packit |
cb6d3d |
int spinner=0;
|
|
Packit |
cb6d3d |
while(elapsed<5000){
|
|
Packit |
cb6d3d |
sectors += readsize;
|
|
Packit |
cb6d3d |
elapsed += time_drive(d, NULL, log, offset, readsize, 0);
|
|
Packit |
cb6d3d |
spinner = elapsed*5/1000%4;
|
|
Packit |
cb6d3d |
printC("\b%c",(spinner==0?'o':(spinner==1?'O':(spinner==2?'o':'.'))));
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
printC("\r");
|
|
Packit |
cb6d3d |
logC("\n");
|
|
Packit |
cb6d3d |
cachems = elapsed/(float)sectors;
|
|
Packit |
cb6d3d |
reportC("\t Cache read speed: %.2fms/sector [%dx]\n",
|
|
Packit |
cb6d3d |
cachems,(int)(1000./75./cachems));
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
if(cachems*3>mspersector){
|
|
Packit |
cb6d3d |
reportC("\tCache access insufficiently faster than media access to\n"
|
|
Packit |
cb6d3d |
"\t\tperform cache backseek tests\n\n");
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
/* now do the read/backseek combo */
|
|
Packit |
cb6d3d |
reportC("\tTesting that backseek flushes cache...");
|
|
Packit |
cb6d3d |
{
|
|
Packit |
cb6d3d |
int total=0;
|
|
Packit |
cb6d3d |
int elapsed=0;
|
|
Packit |
cb6d3d |
int sectors=0;
|
|
Packit |
cb6d3d |
int spinner=0;
|
|
Packit |
cb6d3d |
int retry=0;
|
|
Packit |
cb6d3d |
while(elapsed<5000 && total<25){ /* don't kill the drive */
|
|
Packit |
cb6d3d |
int ret;
|
|
Packit |
cb6d3d |
while(1){
|
|
Packit |
cb6d3d |
/* need to force seek/flush, but don't kill the drive */
|
|
Packit |
cb6d3d |
int seekpos = offset+cachesize+20000;
|
|
Packit |
cb6d3d |
if(seekpos>lastsector-150)seekpos=lastsector-150;
|
|
Packit |
cb6d3d |
ret=cdda_read(d, NULL, seekpos, 1);
|
|
Packit |
cb6d3d |
if(ret>0) ret=time_drive(d, NULL, log, offset+1, readsize, 1);
|
|
Packit |
cb6d3d |
if(ret>=0) ret=time_drive(d, NULL, log, offset, readsize, 1);
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
if(ret<=0){
|
|
Packit |
cb6d3d |
retry++;
|
|
Packit |
cb6d3d |
if(retry==10){
|
|
Packit |
cb6d3d |
reportC("\n\tToo many read errors while performing drive cache checks;"
|
|
Packit |
cb6d3d |
"\n\t aborting test.\n\n");
|
|
Packit |
cb6d3d |
return(-1);
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
reportC("\n\tRead error while performing drive cache checks; retrying...");
|
|
Packit |
cb6d3d |
}else
|
|
Packit |
cb6d3d |
break;
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
sectors += (readsize-1);
|
|
Packit |
cb6d3d |
elapsed += ret;
|
|
Packit |
cb6d3d |
total++;
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
spinner = elapsed*5/1000%4;
|
|
Packit |
cb6d3d |
printC("\b%c",(spinner==0?'o':(spinner==1?'O':(spinner==2?'o':'.'))));
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
printC("\r");
|
|
Packit |
cb6d3d |
logC("\n");
|
|
Packit |
cb6d3d |
readms = elapsed/(float)sectors;
|
|
Packit |
cb6d3d |
reportC("\t Access speed after backseek: %.2fms/sector [%dx]\n",
|
|
Packit |
cb6d3d |
readms,(int)(1000./75./readms));
|
|
Packit |
cb6d3d |
if(readms*2. < mspersector ||
|
|
Packit |
cb6d3d |
cachems*2. > readms){
|
|
Packit |
cb6d3d |
reportC("\tWARNING: Read timing after backseek faster than expected!\n"
|
|
Packit |
cb6d3d |
"\t It's possible/likely that this drive is not\n"
|
|
Packit |
cb6d3d |
"\t flushing the readahead cache on backward seeks!\n\n");
|
|
Packit |
cb6d3d |
warn=1;
|
|
Packit |
cb6d3d |
}else{
|
|
Packit |
cb6d3d |
reportC("\tBackseek flushes the cache as expected\n");
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
}
|
|
Packit |
cb6d3d |
|
|
Packit |
cb6d3d |
return warn;
|
|
Packit |
cb6d3d |
}
|