De IDE driver heeft vijf bronnen met informatie over de geometrie. De eerste (G_user) is datgene wat door de gebruiker op de opdrachtregel is gespecificeerd. De tweede (G_bios) is de BIOS Fixed Disk Parameter Tabel (voor alleen de eerste en de tweede disk) die bij het opstarten van het systeem wordt ingelezen, voor de overschakeling naar 32-bit mode. De derde (G_phys) en vierde (G_log) worden door de IDE controller geretourneerd als een reactie op de opdracht IDENTIFY - dit zijn de `fysieke' en `huidig logische' geometries.
Aan de andere kant heeft de driver twee waarden nodig voor de geometrie:
aan de ene kant G_fdisk, geretourneerd door een
HDIO_GETGEO
ioctl, en aan de andere kant
G_used, die eigenlijk wordt gebruikt voor het doen van I/O.
Beiden, G_fdisk en G_used zijn geïnitialiseerd als G_user als gegeven, aan
G_bios als deze informatie overeenkomstig CMOS aanwezig is, en anders aan
G_phys. Als G_log er redelijk uitziet dan wordt G_used daarop ingesteld
Anders, als G_used redelijk is en G_phys ziet er redelijk
dat wordt G_used ingesteld naar G_phys. Hier betekent `redelijk'
dat het aantal heads zich bevindt in het bereik 1-16.
Met andere woorden: de opdrachtregel overschrijft de BIOS
en zal vaststellen wat fdisk
ziet, maar als het een vertaalde
geometrie specificeert (met meer dan 16 heads), dan zal de vertaalde
geometrie voor kernel I/O door de uitvoer van de opdracht IDENTIFY worden
overschreven.
Merk op dat G_bios nogal onbetrouwbaar is: voor systemen die vanaf SCSI booten kunnen de eerste en tweede disk heel goed SCSI-disks zijn, en de geometrie die de BIOS voor sda rapporteert wordt door de kernel voor hda gebruikt. Bovendien worden disks die niet in de BIOS Setup staan vermeld niet door de BIOS gezien. Dit betekent b.v. dat in een alleen-IDE systeeem waar hdb niet in de Setup is opgegeven, de geometries die door de BIOS voor de eerste en tweede disk worden gerapporteerd, voor hda en hdc zullen gelden.
De situatie voor SCSI is iets anders, aangezien de SCSI opdrachten
reeds logische bloknummers gebruiken, dus een `geometrie' is volledig
irrelevant voor de eigenlijke I/O.
Het formaat van de partitietabel is echter nog steeds hetzelfde,
dus fdisk
moet een bepaalde geometrie uitvinden en gebruikt ook hier
HDIO_GETGEO
-
inderdaad, fdisk
maakt geen onderscheid tussen IDE en SCSI disks.
Zoals men in de hieronderstaande gedetailleerde beschrijving kan zien,
vinden de diverse drivers ieder een iets andere geometrie uit.
Inderdaad, één grote rommel.
Als je geen DOS of iets dergelijks gebruikt, voorkom dan alle uitgebreide vertalingsinstellingen en gebruik gewoon 64 heads, 32 sectoren per spoor ( voor een mooie, gepaste 1 MiB per cylinder) als dit mogelijk is, zodat er geen problemen ontstaan als je de disk van de ene naar de andere controller verplaatst. Een aantal SCSI diskdrivers (aha152x, pas16, ppa, qlogicfas, qlogicisp) zijn zo nerveus over DOS compatibiliteit dat ze het niet toestaan dat een alleen-Linux systeem van meer dan ongeveer 8 GiB gebruiken. Dit is een bug.
Wat is de werkelijke geometrie?
Het makkelijkste antwoord is dat er zoiets niet is.
En als dit wel zo zou zijn, zou je het niet willen weten, en zeker
NOOIT, OOIT fdisk
of LILO of de kernel erover vertellen.
Het is zuiver een zaak tussen de SCSI controller en de disk.
Laat me dat herhalen: alleen domme mensen vertellen fdisk
/LILO/kernel
over de ware SCSI disk geometrie.
Maar als je nieuwsgierig bent en aandringt, zou je het disk zelf kunnen vragen.
Er is de belangrijke opdracht READ CAPACITY dat de totale grootte van
de disk zal geven, en je hebt de MODE SENSE opdracht, dat in de
Rigid Disk Drive Geometry Page (page 04) het aantal cylinders
en heads geeft (dit is informatie die niet kan worden gewijzigd), en geeft in de
Format Page (page 03) het aantal bytes per sector
en sectoren per spoor. Dit laatste nummer is typisch afhankelijk van
de inkeping, en het aantal sectoren per spoor varieert - de buitenste
sporen hebben meer sectoren dan de binnenste sporen.
Het Linux programma scsiinfo
zal je deze informatie geven.
Er zijn veel details en complicaties en het is duidelijk dat niemand
(waarschijnlijk zelfs het besturingssysteem niet) deze informatie wil gebruiken.
Bovendien, zolang als we slechts zijn geïnteresserd in fdisk
en LILO,
krijgt men typisch antwoorden als C/H/S=4476/27/171 - waarden die niet kunnen
worden gebruikt door fdisk
omdat de partitietabel slechts
10 resp. 8 resp. 6 bits voor C/H/S reserveert.
Waar haalt de kernel HDIO_GETGEO
dan zijn informatie vandaan?
Of van de SCSI controller, of door het maken van een ontwikkelde gissing.
Een aantal drivers schijnen te denken dat we de `werkelijkheid' willen
weten, maar natuurlijk willen we slechts weten wat DOS of OS/2 FDISK
(of Adaptec AFDISK, enz) zal gebruiken.
Merk op dat Linux fdisk
de nummers H en S van heads en sectoren
per spoor nodig heeft om LBA sectornummers te vertalen naar c/h/s
adressen, maar het aantal C van cylinders speelt geen rol in deze
conversie. Een aantal drivers gebruiken (C,H,S) = (1023,255 ,63)
om te signaleren dat de drive capaciteit tenminste
1023*
255*
63 sectoren is. Dit is ongelukkig,
aangezien het de werkelijke grootte niet bekend maakt, en zal de limiet van
gebruikers van de meeste fdisk
versies tot ongeveer 8 GiB van
hun disks beperken - tegenwoordig een echte beperking.
In de beschrijving hieronder, duidt de M de totale diskcapaciteit aan,
en C, H, S het aantal cylinders, heads en sectoren per spoor.
Het volstaat om H, S te geven als we C beschouwen als gedefinieerd door
M / (H*
S).
Standaard, H=64, S=32.
H=64, S=32.
H=64, S=32 tenzij C > 1024, in welk geval
H=255, S=63, C = min(1023, M/(H*
S)).
(Dus C is afgekapt, en H*
S*
C is geen benaderde waarde van
de diskcapaciteit M.
Dit zal de meeste versies van fdisk
in de war
brengen.)
De ppa.c
code gebruikt M+1 in plaats van M en zegt dat te wijten aan
een bug in sd.c
M is uit door 1.
H=64, S=32 tenzij C > 1024 en bovendien de `> 1 GB' optie in de BIOS is geactiveerd, in welk geval H=255, S=63.
Vraag de controller welke van de twee mogelijke vertaalschema's in gebruik is, en gebruik H=255, S=63 of H=64, S=32. In het eerste geval krijg je een bootmelding "aha1542.c: Using extended bios translation".
H=64, S=32 tenzij C > 1024, en bovendien of de "extended" boot parameter was opgegeven, of de `extended' bit in de SEEPROM van de BIOS werd ingesteld, in welk geval H=255, S=63. In Linux 2.0.36 zou deze extended vertaling altijd ingesteld moeten zijn voor het geval geen SEEPROM werd gevonden, maar in Linux 2.2.6 wordt als er geen SEEPROM is gevonden, extended vertaling alleen ingesteld als de gebruiker erom vraagt door deze bootparameter te gebruiken (terwijl als een SEEPROM wordt gevonden, de bootparameter wordt genegeerd). Dit kan betekenen dat een setup die onder 2.0.36 werkt niet boot met 2.2.6 (en het `linear' sleutelwoord voor LILO vereist, of de `aic7xxx=extended' kernel boot parameter).
H=64, S=32 tenzij C >= 1024, en bovendien extended vertaling werd geactiveerd op de controller, in welk geval als M < 2^22 dan H=128, S=32; anders H=255, S=63. Echter na het maken van deze keuze voor (C,H,S), wordt de partitietabel ingelezen, en als voor één van de drie mogelijkheden (H,S) = (64,32), (128,32), (255,63) de waarde endH=H-1 ergens wordt gezien dan wordt dat paar (H,S) gebruikt, en wordt de bootmelding "Adopting Geometry from Partition Table" afgedrukt.
Zoek de geometrie informatie op in de BIOS Drive Parameter Tabel,
of lees de partitietabel in en gebruik H=endH+1, S=endS voor de eerste
partitie, op voorwaarde dat het niet leeg is, of gebruik H=64, S=32
voor M < 2^21 (1 GiB),
H=128, S=63 voor M < 63*
2^17 (3.9 GiB) en anders H=255, S=63.
Gebruik de eerste van (H,S) = (64,32), (64,63), (128,63), (255,63) dat zal C <= 1024 maken. Kap in het laatste geval C af bij 1023.
Lees C,H,S vanaf de disk. (Griezels!) Als C of S te groot is, zet dan
S=17, H=2 en verdubbel H tot C <= 1024. Dit betekent dat H op 0 zal
worden ingesteld als M > 128*
1024*
17 (1.1 GiB). Dit is een bug.
Een van drie afbeeldingen ((H,S) = (16,63), (64,32), (64,63)) wordt gebruikt afhankelijk van de controller afbeeldingsmode.
Kijk naar de partitietabel. Aangezien volgens afspraak partities eindigen
op een cylindergrens, kunnen we, gegeven end = (endC,endH,endS)
voor enige partitie, gewoon zetten H = endH+1
en S = endS
.
(Ter herinnering dat sectoren worden geteld vanaf 1.)
Preciezer, het volgende wordt gedaan.
Als er een niet-lege partitie is,
neem de partitie met de grootste beginC
.
Voor die partitie, kijk naar end+1
, berekend
door het toevoegen van start
en lengte
en door ervan uit te gaan dat
deze partitie op een cylindergrens eindigt.
Als beide waarden overeenkomen, of
als endC
= 1023 en start+lengte
een integrale veelvoud van
(endH+1)
is,
ga er dan vanuit dat deze partitie echt was aangepast op een cylindergrens
en zet H = *
endSendH+1
en S = endS
.
Als dit niet werkt, of omdat er geen partities zijn, of omdat ze vreemde
groottes hebben, kijk dan slechts naar de diskcapaciteit M.
Algorithme: zet H = M/(62*
1024) (naar boven afgerond), S = M/(1024*
H)
(naar boven afgerond), C = M/(H*
S) (naar beneden afgerond).
Dit heeft als effect het produceren van een (C,H,S) met C ten hoogste 1024
en S ten hoogste 62.