PCI Latency Timer, MAX_LAT ve MIN_GNT
by faik, 02.15.07 at 8:50 pm :: Çekirdek :: permalink :: rss
PCI cihaz bus’a erişmek istediği anda REQ# hattı üzerinden arbiter‘a istekte bulunuyor. Arbiter’da PCI speklerinde implimentasyona bırakılan adil bir şekilde cihazların isteklerini GNT# hattını aktif hale getirerek karşılıyor. Eğer GNT# aktif hale getirildikten 16 PCI clock sonra bir işlem başlamaz ise, arbiter, cihazı “kırık” olarak kabul edip sonraki istekleri gözardı edebilir.Cihaz GNT# aldıktan sonra adres fazı sonrası, data fazına başlayacak. Cihaz izni alana kadar diğer cihazların devam eden işlemlerinin bitmesini bekledi, arbiter’ın kendisine bus’ı vermesini bekledi. Eğer bu kadar zaman bekledikten sonra, bus’ı alıp da, DWORD boyutunda tek bir veri transfer işlemi yapacak olsa cihaz açısından performans oldukça düşük olacaktır. Bunun için veri transferi “burst” modda gerçekleştiriliyor. Başlayan transferin veri fazı ardından ardışıl adreslere veri transferi devam ediyor.
Peki nereye kadar? Bunu belirlemek için PCI “bus-mastering” özelliğine sahip ve iki data fazından fazla burst edebilecek cihazların bir zamanlayıcı barındırması gerekiyor. Bunun ismi “Latency Timer”. Timer’ın sayacağı değer ise cihazın “configuration space”‘inde saklı. Her transfer başlangıcında (FRAME# aktif hale getirildiğinde) timer yeniden başlıyor. Timer sona erdiğinde cihaz işlemi sonlandırıyor. (Aslında zamanlayıcı sonlandırma için tek parametre değil. O an yürütülen PCI işlem cinsine göre de gelebilen bir takım parametreler daha var. Ayrıntılar spekte ve yazılımsal açıdan baktığımızda çok da önemli değil.) Daha önce de işini bitirebilir ama timer dolduğunda bus’ı bırakmak zorunda.
Genel olarak baktığımızda burada ortaya çıkan “Latency Timer”‘ın cihazın ne kadar bus’a sahip olacağı üzerinde önemli bir etkisinin olması. Eğer bu değer çok kısa olursa, diğer bekleyen cihazların gecikmesi azalıyor, eğer fazla olursa daha fazla veri gönderimi yapılıp, cihaz açısından performans artışı oluyor. “Latency” ve “Throughput” dengesi söz konusu.
Peki bu değeri kim ve neye göre belirliyor? Değeri asıl belirleyen BIOS. Açılış sırasında bu değeri “configuration space”‘de tüm cihazların ilgili offsetlerine yazıyor. Bunun dışında nadiren çekirdek sürücüleri de tanıdığı cihaz için bu değer ile oynayarak bir nevi “fine tuning” yapabiliyor. Değeri root hakları ile kullanıcı da “setpci” komutu aracılığı ile yazabilir. Çok nadir durumlarda ses kartı ile ilgili ve ya görüntü yakalama kartları ile ilgili gecikme problemlerini çözmek için kullanılabilir. Ama değerle oynamak çoğu zaman gereksiz. “Latency timer” register’ı “configuration space” 0Dh offsetinde bulunuyor.
faik@iago ~ $ /usr/sbin/lspci -s 0a:08.0 -xxx
0a:08.0 Ethernet controller: Intel Corporation PRO/100 VE Network Connection (rev 02)
00: 86 80 92 10 07 00 90 02 02 00 00 02 10 40 00 00
10: 00 50 00 d2 01 60 00 00 00 00 00 00 00 00 00 00
20: 00 00 00 00 00 00 00 00 00 00 00 00 4d 10 ef 81
30: 00 00 00 00 dc 00 00 00 00 00 00 00 0b 01 08 38
faik@iago ~ $ sudo /usr/sbin/setpci -s 0a:08.0 latency_timer=80
faik@iago ~ $ /usr/sbin/lspci -s 0a:08.0 -xxx
0a:08.0 Ethernet controller: Intel Corporation PRO/100 VE Network Connection (rev 02)
00: 86 80 92 10 07 00 90 02 02 00 00 02 10 80 00 00
10: 00 50 00 d2 01 60 00 00 00 00 00 00 00 00 00 00
20: 00 00 00 00 00 00 00 00 00 00 00 00 4d 10 ef 81
30: 00 00 00 00 dc 00 00 00 00 00 00 00 0b 01 08 38
Bu değer cihazın ihtiyaçlarına göre belirlenmesi gereken bir değer. Cihazın ihtiyaçlarını da BIOS’un ya da çekirdeğin anlayabilmesi için cihaz’ın “configuration space”‘inde tanımlı iki adet register mevcut. Bunlar MAX_LAT ve MIN_GNT registerları. Sırasıyla 3Fh ve 3Eh offsetlerinde bulunuyorlar.
MIN_GNT bir cihazın ne uzunlukta “burst” zamanına ihtiyacı olduğunu 33 Mhz’lik PCI clock referansı ile 8 PCI clock birim cinsinden saklıyor. Sebebi aşağıdaki örnek ile daha rahat anlaşılıyor. MAX_LAT ise cihazın ne sıklıkta bus’a ihtiyacı olduğunu belirtiyor.
PCI speklerinde çok güzel bir örnek verilmiş. 100 Mbs’lik bir ethernet cihazı yaptığınızı düşünün. Yaklaşık 10 MB/s lik bir “throughput” karşılamak istiyorsunuz. Ethernet’inizin de her iki yön için 64-byte boyutunda bufferları bulunuyor. Her bir buffer’ı iki 32-bytelık bölme olarak (ping-pong buffer) yapmak optimum performansı sağlıyor denilmiş. Rx buffer’ı düşünün. 32-byte yazılan diğer 32-byte’lık bölmeye kaydı. O işlenirken aynı zamanda önceki 32-byte’da doldurulabiliyor. Her 32-byte transfer 8 DWORD’e tekabul ediyor. Bu da 8 veri fazı/8 clock demek. 33 Mhz için 1/4 mikro saniyelik ( 1/33 Mhz * 8 ) bir zamana denk geliyor. Birim olarak en az 8 clock alınmış. Yani MIN_GNT 1 olmalı. Eğer böyle olursa cihaz istemiş olduğu 32-bytelık “burst” transferi bu zaman içinde bir seferde gerçekleştirebilir. 10 MB/s lik “throughput” için ise 32-byte’lık buffer için 32/10 MB/s ile 3.2 mikro saniyede bir “burst” yapmak yeterli geliyor. Bu da yaklaşık MAX_LAT için 12 değerine denk geliyor. (3.2 / 0.25) MAX_LAT ve MIN_GNT yazmaçları sadece okunabiliyor. Cihaz karakterini belirliyor.
Çekirdek açılışında görünen şu mesajlar, çekirdeğin ayarladığı değeri bildiriyor:
PCI: Setting latency timer of device 0000:00:1d.1 to 64
[arch/i386/pci/i386.c]
/*
* If we set up a device for bus mastering, we need to check the latency
* timer as certain crappy BIOSes forget to set it properly.
*/
unsigned int pcibios_max_latency = 255;
void pcibios_set_master(struct pci_dev *dev)
{
u8 lat;
pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
if (lat < 16)
lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency;
else if (lat > pcibios_max_latency)
lat = pcibios_max_latency;
else
return;
printk(KERN_DEBUG "PCI: Setting latency timer of device %s to %d\n",
pci_name(dev), lat);
pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
}
Çekirdeğin ayarlamasında anladığım iki sebep var. Birincisi comment’de belirtildiği gibi bazı BIOS’ların yanlış set etmesi ve ya hiç etmemesi. İkinci sebebi ise bazı chipsetlerdeki sorunlara workaround yapabilmek için. Örneğin bazı SiS chipsetlerinde latency timer 32′den büyük olursa bus’da kilitlenme olabiliyormuş.
[arch/i386/pci/fixup.c]
static void __devinit pci_fixup_latency(struct pci_dev *d)
{
/*
* SiS 5597 and 5598 chipsets require latency timer set to
* at most 32 to avoid lockups.
*/
DBG("PCI: Setting max latency to 32\n");
pcibios_max_latency = 32;
}
BIOS tarafında özel bir şey var mı diye LinuxBIOS’a baktığımda öntanımlı olarak tüm timerları 0×40 olarak ayarladığını görüyorum. Hani tüm cihazlara bakılıp bir algoritmayla optimum paylaşım için değerler hesaplayıp ayrı ayrı yazma gibi bir şey yok.
[src/devices/pci_device.c]
void pci_dev_set_resources(struct device *dev)…
/* set a default latency timer */
pci_write_config8(dev, PCI_LATENCY_TIMER, 0×40);
Daha inceleme fırsatı bulamadığım PCI Express mimarisinde MIN_GNT ve MAX_LAT bir şey ifade etmiyor. PCI Express yapısı PCI’ya göre oldukça farklı. İnceleyince daha ayrıntılı görürüz.


No comments at the moment.
Add a comment