CINXE.COM
LKML: Ochal Christophe: Re: Possible bug in amd64-agp (agpgart module)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>LKML: Ochal Christophe: Re: Possible bug in amd64-agp (agpgart module)</title><link href="/css/message.css" rel="stylesheet" type="text/css" /><link href="/css/wrap.css" rel="alternate stylesheet" type="text/css" title="wrap" /><link href="/css/nowrap.css" rel="stylesheet" type="text/css" title="nowrap" /><link href="/favicon.ico" rel="shortcut icon" /><script src="/js/simple-calendar.js" type="text/javascript"></script><script src="/js/styleswitcher.js" type="text/javascript"></script><link rel="alternate" type="application/rss+xml" title="lkml.org : last 100 messages" href="/rss.php" /><link rel="alternate" type="application/rss+xml" title="lkml.org : last messages by Ochal Christophe" href="/groupie.php?aid=31112" /><!--Matomo--><script> var _paq = window._paq = window._paq || []; /* tracker methods like "setCustomDimension" should be called before "trackPageView" */ _paq.push(["setDoNotTrack", true]); _paq.push(["disableCookies"]); _paq.push(['trackPageView']); _paq.push(['enableLinkTracking']); (function() { var u="//m.lkml.org/"; _paq.push(['setTrackerUrl', u+'matomo.php']); _paq.push(['setSiteId', '1']); var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); })(); </script><!--End Matomo Code--></head><body onload="es.jasper.simpleCalendar.init();" itemscope="itemscope" itemtype="http://schema.org/BlogPosting"><table border="0" cellpadding="0" cellspacing="0"><tr><td width="180" align="center"><a href="/"><img style="border:0;width:135px;height:32px" src="/images/toprowlk.gif" alt="lkml.org" /></a></td><td width="32">聽</td><td class="nb"><div><a class="nb" href="/lkml"> [lkml]</a> 聽 <a class="nb" href="/lkml/2005"> [2005]</a> 聽 <a class="nb" href="/lkml/2005/12"> [Dec]</a> 聽 <a class="nb" href="/lkml/2005/12/31"> [31]</a> 聽 <a class="nb" href="/lkml/last100"> [last100]</a> 聽 <a href="/rss.php"><img src="/images/rss-or.gif" border="0" alt="RSS Feed" /></a></div><div>Views: <a href="#" class="nowrap" onclick="setActiveStyleSheet('wrap');return false;">[wrap]</a><a href="#" class="wrap" onclick="setActiveStyleSheet('nowrap');return false;">[no wrap]</a> 聽 <a class="nb" href="/lkml/mheaders/2005/12/31/88" onclick="this.href='/lkml/headers'+'/2005/12/31/88';">[headers]</a>聽 <a href="/lkml/bounce/2005/12/31/88">[forward]</a>聽 </div></td><td width="32">聽</td></tr><tr><td valign="top"><div class="es-jasper-simpleCalendar" baseurl="/lkml/"></div><div class="threadlist">Messages in this thread</div><ul class="threadlist"><li class="root"><a href="/lkml/2005/12/30/42">First message in thread</a></li><li><a href="/lkml/2005/12/30/42">Ochal Christophe</a><ul><li class="origin"><a href="">Ochal Christophe</a></li></ul></li></ul></td><td width="32" rowspan="2" class="c" valign="top"><img src="/images/icornerl.gif" width="32" height="32" alt="/" /></td><td class="c" rowspan="2" valign="top" style="padding-top: 1em"><table><tr><td><table><tr><td class="lp">Date</td><td class="rp" itemprop="datePublished">Sat, 31 Dec 2005 18:13:43 +0100</td></tr><tr><td class="lp">From</td><td class="rp" itemprop="author">Ochal Christophe <></td></tr><tr><td class="lp">Subject</td><td class="rp" itemprop="name">Re: Possible bug in amd64-agp (agpgart module)</td></tr></table></td><td></td></tr></table><pre itemprop="articleBody">Hi all,<br /><br />Some more info regarding my uphill battle with my machine :)<br /><br />Ochal Christophe wrote:<br /><br />> Hi all,<br />><br />> Sorry for the intrusion, but i think i've stumbled across a bug in the <br />> linux kernel.<br />> On an Asus KV8-S XE motherboard, the agpgart module is unable to <br />> properly determine the AGP aperture size, regardless of bios options <br />> (AGP 8x mode & AGP 4x mode), this *might* also be related to possible <br />> hangs in X11 when using DRI.<br />><br />> Bug was seen on the following config:<br />><br />> Asus K8V-S XE motherboard, 512MB ram with AMD Sempron 3200 (Athlon64 <br />> core)<br />> Witnessed with an ATI Radeon 9700 Pro card and with an Asus Radeon <br />> 9200 SE.<br />><br />> I'll try & test this config with some more gfx cards to see if the gfx <br />> card has any influence on the detection, but i'm not sure i have any <br />> nVidia cards available.<br />><br />> This phenomenon has been found in atleast the following kernel versions:<br />><br />> linux 2.6.12-r9<br />> linux 2.6.14-r4<br />> linux 2.6.14-r5<br />> linux 2.6.14-r7<br />><br />> The reported aperture size is always 32M, more details can be <br />> provided, just ask what you need to know. <br /><br />Some more info, might be worthwhile for the people in the know:<br /><br />Motherboard: Asus K8V-X SE<br />lspci:<br />00:00.0 Host bridge: VIA Technologies, Inc. K8T800Pro Host Bridge<br />00:00.1 Host bridge: VIA Technologies, Inc. K8T800Pro Host Bridge<br />00:00.2 Host bridge: VIA Technologies, Inc. K8T800Pro Host Bridge<br />00:00.3 Host bridge: VIA Technologies, Inc. K8T800Pro Host Bridge<br />00:00.4 Host bridge: VIA Technologies, Inc. K8T800Pro Host Bridge<br />00:00.7 Host bridge: VIA Technologies, Inc. K8T800Pro Host Bridge<br />00:01.0 PCI bridge: VIA Technologies, Inc. VT8237 PCI bridge <br />[K8T800/K8T890 South]<br />00:0f.0 RAID bus controller: VIA Technologies, Inc. VIA VT6420 SATA RAID <br />Controller (rev 80)<br />00:0f.1 IDE interface: VIA Technologies, Inc. <br />VT82C586A/B/VT82C686/A/B/VT823x/A/C PIPC Bus Master IDE (rev 06)<br />00:10.0 USB Controller: VIA Technologies, Inc. VT82xxxxx UHCI USB 1.1 <br />Controller (rev 81)<br />00:10.1 USB Controller: VIA Technologies, Inc. VT82xxxxx UHCI USB 1.1 <br />Controller (rev 81)<br />00:10.2 USB Controller: VIA Technologies, Inc. VT82xxxxx UHCI USB 1.1 <br />Controller (rev 81)<br />00:10.3 USB Controller: VIA Technologies, Inc. VT82xxxxx UHCI USB 1.1 <br />Controller (rev 81)<br />00:10.4 USB Controller: VIA Technologies, Inc. USB 2.0 (rev 86)<br />00:11.0 ISA bridge: VIA Technologies, Inc. VT8237 ISA bridge <br />[KT600/K8T800/K8T890 South]<br />00:11.5 Multimedia audio controller: VIA Technologies, Inc. <br />VT8233/A/8235/8237 AC97 Audio Controller (rev 60)<br />00:12.0 Ethernet controller: VIA Technologies, Inc. VT6102 [Rhine-II] <br />(rev 78)<br />00:18.0 Host bridge: Advanced Micro Devices [AMD] K8 [Athlon64/Opteron] <br />HyperTransport Technology Configuration<br />00:18.1 Host bridge: Advanced Micro Devices [AMD] K8 [Athlon64/Opteron] <br />Address Map<br />00:18.2 Host bridge: Advanced Micro Devices [AMD] K8 [Athlon64/Opteron] <br />DRAM Controller<br />00:18.3 Host bridge: Advanced Micro Devices [AMD] K8 [Athlon64/Opteron] <br />Miscellaneous Control<br />01:00.0 VGA compatible controller: ATI Technologies Inc Radeon R300 ND <br />[Radeon 9700 Pro]<br />01:00.1 Display controller: ATI Technologies Inc Radeon R300 [Radeon <br />9700 Pro] (Secondary)<br /><br />Distribution:<br />Gentoo with kernel: 2.6.13-gentoo-r5<br /><br />Problem witnessed with multiple kernels (see prior post), regardless of <br />BIOS settings.<br /><br />Test results of testgart.c:<br /><br />localhost ochal # ./test<br />version: 0.101<br />bridge id: 0x2821106<br />agp_mode: 0x1f000a1b<br />aper_base: 0xe4000000<br />aper_size: 32<br />pg_total: 112384<br />pg_system: 112384<br />pg_used: 0<br />entry.key : 0<br />entry.key : 1<br />Allocated 8 megs of GART memory<br />MemoryBenchmark: 8 mb/s<br />MemoryBenchmark: 8 mb/s<br />MemoryBenchmark: 9 mb/s<br />Average speed: 8 mb/s<br />Testing data integrity (1st pass): failed on first pass!<br />Testing data integrity (2nd pass): failed on second pass!<br /><br />dmesg:<br />Linux agpgart interface v0.101 (c) Dave Jones<br />fglrx: module license 'Proprietary. (C) 2002 - ATI Technologies, <br />Starnberg, GERMANY' taints kernel.<br />[fglrx] Maximum main memory to use for locked dma buffers: 430 MBytes.<br />ACPI: PCI Interrupt 0000:01:00.0[A] -> GSI 16 (level, low) -> IRQ 201<br />[fglrx] module loaded - fglrx 8.20.8 [Dec 6 2005] on minor 0<br />[fglrx] ACPI power management is initialized.<br />Fire GL agpgart support<br />fglrx_agp is probing for an AGP device<br />Device found = 1106<br />failed pci_module_init<br />Unloading fglrx_agp<br />firegl_agp_probe failed<br />[fglrx] Failed to load fglrx_agp module<br />[fglrx] Error code 256<br />[fglrx] Failed to load ATI module agpgart<br />[fglrx] Fallback to internal agpgart module<br />Fire GL built-in AGP-support<br />Based on agpgart interface v0.99 (c) Jeff Hartmann<br />agpgart: Maximum main memory to use for agp memory: 439M<br />agpgart: Unsupported Via chipset (device id: 0282), you might want to <br />try agp_try_unsupported=1.<br />agpgart: no supported devices found.<br />[fglrx] Initialization of built-in AGP-support failed (ret=-19).<br />[fglrx:firegl_unlock] *ERROR* Process 5366 using kernel context 0<br />scsi: unknown opcode 0xe9<br />scsi: unknown opcode 0xed<br />scsi: unknown opcode 0x01<br />scsi: unknown opcode 0xf5<br />hda: packet command error: status=0x51 { DriveReady SeekComplete Error }<br />hda: packet command error: error=0x54 { AbortedCommand <br />LastFailedSense=0x05 }<br />ide: failed opcode was: unknown<br />hda: packet command error: status=0x51 { DriveReady SeekComplete Error }<br />hda: packet command error: error=0x54 { AbortedCommand <br />LastFailedSense=0x05 }<br />ide: failed opcode was: unknown<br />agpgart: Detected AGP bridge 0<br />agpgart: AGP aperture is 32M @ 0xe4000000<br />agpgart: Found an AGP 3.0 compliant device at 0000:00:00.0.<br />agpgart: test tried to set rate=x12. Setting to AGP3 x8 mode.<br />agpgart: Putting AGP V3 device at 0000:00:00.0 into 8x mode<br />agpgart: Putting AGP V3 device at 0000:01:00.0 into 8x mode<br />agpgart: Found an AGP 3.0 compliant device at 0000:00:00.0.<br />agpgart: test tried to set rate=x12. Setting to AGP3 x8 mode.<br />agpgart: Putting AGP V3 device at 0000:00:00.0 into 8x mode<br />agpgart: Putting AGP V3 device at 0000:01:00.0 into 8x mode<br /><br /><br />The testgart.c code:<br /><br />/* <br /> * <br /> * Test program for AGPGART module under Linux<br /> * <br /> * Copyright (C) 1999 Jeff Hartmann, <br /> * Precision Insight, Inc., Xi Graphics, Inc.<br /> *<br /> */ <br /><br /><br />#include <stdio.h><br />#include <unistd.h><br />#include <sys/ioctl.h><br />#include <fcntl.h><br />#include <sys/mman.h><br />#include <sys/time.h><br />#include <linux/types.h><br />#include <linux/agpgart.h><br />#include <asm/mtrr.h><br />#include <errno.h><br /><br />unsigned char *gart;<br />int gartfd;<br />int mtrr;<br /><br />int usec( void ) {<br /> struct timeval tv;<br /> struct timezone tz;<br /> <br /> gettimeofday( &tv, &tz );<br /> return (tv.tv_sec & 2047) * 1000000 + tv.tv_usec;<br />}<br /><br />int MemoryBenchmark( void *buffer, int dwords ) {<br /> int i;<br /> int start, end;<br /> int mb;<br /> int *base;<br /> <br /> base = (int *)buffer;<br /> start = usec();<br /> for ( i = 0 ; i < dwords ; i += 8 ) {<br /> base[i] =<br /> base[i+1] =<br /> base[i+2] =<br /> base[i+3] =<br /> base[i+4] =<br /> base[i+5] =<br /> base[i+6] =<br /> base[i+7] = 0x15151515; /* dmapad nops */<br /><br /> }<br /> end = usec();<br /> mb = ( (float)dwords / 0x40000 ) * 1000000 / (end - start);<br /> printf("MemoryBenchmark: %i mb/s\n", mb );<br /> return mb;<br />}<br /><br />int insert_gart(int page, int size)<br />{<br /> agp_allocate entry;<br /> agp_bind bind;<br /> <br /> entry.type = 0;<br /> entry.pg_count = size;<br />#ifdef DEBUG<br /> printf("Using AGPIOC_ALLOCATE\n");<br />#endif<br /> if(ioctl(gartfd, AGPIOC_ALLOCATE, &entry) != 0)<br /> {<br /> perror("ioctl(AGPIOC_ALLOCATE)");<br /> exit(1);<br /> }<br /> <br /> bind.key = entry.key;<br /> bind.pg_start = page;<br />#ifdef DEBUG<br /> printf("Using AGPIOC_BIND\n");<br />#endif<br /> if(ioctl(gartfd, AGPIOC_BIND, &bind))<br /> {<br /> perror("ioctl(AGPIOC_BIND)");<br /> exit(1);<br /> }<br /> <br /> printf("entry.key : %i\n", entry.key);<br /> <br /> return(entry.key);<br />}<br /><br />int unbind_gart(int key)<br />{<br /> agp_unbind unbind;<br /> <br /> unbind.key = key;<br />#ifdef DEBUG<br /> printf("Using AGPIOC_UNBIND\n");<br />#endif<br /> if(ioctl(gartfd, AGPIOC_UNBIND, &unbind) != 0)<br /> {<br /> perror("ioctl(AGPIOC_UNBIND)");<br /> exit(1);<br /> }<br /> <br /> return(0);<br />}<br /><br />int bind_gart(int key, int page)<br />{<br /> agp_bind bind;<br /> <br /> bind.key = key;<br /> bind.pg_start = page;<br />#ifdef DEBUG<br /> printf("Using AGPIOC_BIND\n");<br />#endif<br /> if(ioctl(gartfd, AGPIOC_BIND, &bind) != 0)<br /> {<br /> perror("ioctl(AGPIOC_BIND)");<br /> exit(1);<br /> }<br /> <br /> return(0);<br />}<br /><br />int remove_gart(int key)<br />{<br />#ifdef DEBUG<br /> printf("Using AGPIOC_DEALLOCATE\n");<br />#endif<br /> if(ioctl(gartfd, AGPIOC_DEALLOCATE, key) != 0)<br /> {<br /> perror("ioctl(GARTIOCREMOVE)");<br /> exit(1);<br /> }<br /> <br /> return(0);<br />}<br /><br />void openmtrr(void) <br />{<br /> if ((mtrr = open("/proc/mtrr", O_WRONLY, 0)) == -1) <br /> {<br /> if (errno == ENOENT) {<br /> perror("/proc/mtrr not found: MTRR not enabled\n");<br /> } else {<br /> perror("Error opening /proc/mtrr:");<br /> perror("MTRR not enabled\n");<br /> exit(1);<br /> }<br /> return;<br /> }<br />}<br /><br />int CoverRangeWithMTRR( int base, int range, int type )<br />{<br /> int count; <br /> <br /> /* set it if we aren't just checking the number */<br /> if ( type != -1 ) {<br /> struct mtrr_sentry sentry;<br /> <br /> sentry.base = base;<br /> sentry.size = range;<br /> sentry.type = type;<br /> <br /> if ( ioctl(mtrr, MTRRIOC_ADD_ENTRY, &sentry) == -1 ) {<br /> perror("mtrr");<br /> exit(1);<br /> }<br /> }<br /> <br />}<br /><br />int init_agp(void)<br />{<br /> agp_info info;<br /> agp_setup setup;<br /><br />#ifdef DEBUG<br /> printf("Using AGPIOC_ACQUIRE\n");<br />#endif<br /> if(ioctl(gartfd, AGPIOC_ACQUIRE) != 0)<br /> {<br /> perror("ioctl(AGPIOC_ACQUIRE)");<br /> exit(1);<br /> }<br />#ifdef DEBUG<br /> printf("Using AGPIOC_INFO\n");<br />#endif<br /> if(ioctl(gartfd, AGPIOC_INFO, &info) != 0)<br /> {<br /> perror("ioctl(AGPIOC_INFO)");<br /> exit(1);<br /> }<br /> <br /> printf("version: %i.%i\n", info.version.major, info.version.minor);<br /> printf("bridge id: 0x%lx\n", info.bridge_id);<br /> printf("agp_mode: 0x%lx\n", info.agp_mode);<br /> printf("aper_base: 0x%lx\n", info.aper_base);<br /> printf("aper_size: %i\n", info.aper_size);<br /> printf("pg_total: %i\n", info.pg_total);<br /> printf("pg_system: %i\n", info.pg_system);<br /> printf("pg_used: %i\n", info.pg_used);<br /><br /> openmtrr();<br /> if (mtrr != -1) { <br /> CoverRangeWithMTRR(info.aper_base, info.aper_size * 0x100000, <br /> MTRR_TYPE_WRCOMB);<br /> }<br /><br /> gart = mmap(NULL, info.aper_size * 0x100000, PROT_READ | PROT_WRITE, MAP_SHARED, gartfd, 0);<br /><br /> if(gart == (unsigned char *) 0xffffffff)<br /> {<br /> perror("mmap");<br /> close(gartfd);<br /> exit(1);<br /> } <br /> <br /> setup.agp_mode = info.agp_mode;<br />#ifdef DEBUG<br /> printf("Using AGPIOC_SETUP\n");<br />#endif<br /> if(ioctl(gartfd, AGPIOC_SETUP, &setup) != 0)<br /> {<br /> perror("ioctl(AGPIOC_SETUP)");<br /> exit(1);<br /> }<br /> <br /> return(0);<br />}<br /><br />int xchangeDummy;<br /><br />void FlushWriteCombining( void ) {<br /> __asm__ volatile( " push %%eax ; xchg %%eax, %0 ; pop %%eax" : : "m" (xchangeDummy));<br /> __asm__ volatile( " push %%eax ; push %%ebx ; push %%ecx ; push %%edx ; movl $0,%%eax ; cpuid ; pop %%edx ; pop %%ecx ; pop %%ebx ; pop %%eax" : /* no outputs */ : /* no inputs */ );<br />}<br /><br />void BenchMark()<br />{<br /> int i, worked = 1;<br /><br /> i = MemoryBenchmark(gart, (1024 * 1024 * 4) / 4) +<br /> MemoryBenchmark(gart, (1024 * 1024 * 4) / 4) +<br /> MemoryBenchmark(gart, (1024 * 1024 * 4) / 4);<br /> <br /> printf("Average speed: %i mb/s\n", i /3);<br /> <br /> printf("Testing data integrity (1st pass): ");<br /> fflush(stdout);<br /> <br /> FlushWriteCombining();<br /> <br /> for (i=0; i < 8 * 0x100000; i++)<br /> {<br /> gart[i] = i % 256;<br /> }<br /> <br /> FlushWriteCombining();<br /> <br /> <br /> for (i=0; i < 8 * 0x100000; i++)<br /> {<br /> if(!(gart[i] == i % 256))<br /> {<br />#ifdef DEBUG<br /> printf("failed on %i, gart[i] = %i\n", i, gart[i]);<br />#endif<br /> worked = 0;<br /> }<br /> }<br /> <br /> if (!worked)<br /> printf("failed on first pass!\n");<br /> else<br /> printf("passed on first pass.\n");<br /> <br /> unbind_gart(0);<br /> unbind_gart(1);<br /> bind_gart(0, 0);<br /> bind_gart(1, 1024);<br /><br /> worked = 1;<br /> printf("Testing data integrity (2nd pass): ");<br /> fflush(stdout);<br /> <br /> for (i=0; i < 8 * 0x100000; i++)<br /> {<br /> if(!(gart[i] == i % 256))<br /> {<br />#ifdef DEBUG<br /> printf("failed on %i, gart[i] = %i\n", i, gart[i]);<br />#endif<br /> worked = 0;<br /> }<br /> }<br /><br /> if (!worked)<br /> printf("failed on second pass!\n");<br /> else<br /> printf("passed on second pass.\n");<br />}<br /><br />int main()<br />{<br /> int i;<br /> int key;<br /> int key2;<br /> agp_info info;<br /> <br /> gartfd = open("/dev/agpgart", O_RDWR);<br /> if (gartfd == -1)<br /> { <br /> perror("open");<br /> exit(1);<br /> }<br /> <br /> init_agp();<br /> <br /> key = insert_gart(0, 1024);<br /> key2 = insert_gart(1024, 1024);<br /> <br />#ifdef DEBUG<br /> printf("Using AGPIOC_INFO\n");<br /> if(ioctl(gartfd, AGPIOC_INFO, &info) != 0)<br /> {<br /> perror("ioctl(AGPIOC_INFO)");<br /> exit(1);<br /> }<br /> <br /> printf("version: %i.%i\n", info.version.major, info.version.minor);<br /> printf("bridge id: 0x%lx\n", info.bridge_id);<br /> printf("agp_mode: 0x%lx\n", info.agp_mode);<br /> printf("aper_base: 0x%lx\n", info.aper_base);<br /> printf("aper_size: %i\n", info.aper_size);<br /> printf("pg_total: %i\n", info.pg_total);<br /> printf("pg_system: %i\n", info.pg_system);<br /> printf("pg_used: %i\n", info.pg_used);<br />#endif<br /> <br /> printf("Allocated 8 megs of GART memory\n");<br /> <br /> BenchMark();<br /> <br /> remove_gart(key);<br /> remove_gart(key2);<br /><br />#ifdef DEBUG <br /> printf("Using AGPIOC_RELEASE\n");<br />#endif<br /> if(ioctl(gartfd, AGPIOC_RELEASE) != 0)<br /> {<br /> perror("ioctl(AGPIOC_RELEASE)");<br /> exit(1);<br /> }<br /> <br /> close(gartfd);<br />}<br /><br /><br />I don't know how correct this test program is, i'm willing to test the <br />hell out of this.<br />-<br />To unsubscribe from this list: send the line "unsubscribe linux-kernel" in<br />the body of a message to majordomo@vger.kernel.org<br />More majordomo info at <a href="http://vger.kernel.org/majordomo-info.html">http://vger.kernel.org/majordomo-info.html</a><br />Please read the FAQ at <a href="http://www.tux.org/lkml/">http://www.tux.org/lkml/</a><br /><br /></pre></td><td width="32" rowspan="2" class="c" valign="top"><img src="/images/icornerr.gif" width="32" height="32" alt="\" /></td></tr><tr><td align="right" valign="bottom"> 聽 </td></tr><tr><td align="right" valign="bottom">聽</td><td class="c" valign="bottom" style="padding-bottom: 0px"><img src="/images/bcornerl.gif" width="32" height="32" alt="\" /></td><td class="c">聽</td><td class="c" valign="bottom" style="padding-bottom: 0px"><img src="/images/bcornerr.gif" width="32" height="32" alt="/" /></td></tr><tr><td align="right" valign="top" colspan="2"> 聽 </td><td class="lm">Last update: 2005-12-31 17:16 聽聽 [from the cache]<br />漏2003-2020 <a href="http://blog.jasper.es/"><span itemprop="editor">Jasper Spaans</span></a>|hosted at <a href="https://www.digitalocean.com/?refcode=9a8e99d24cf9">Digital Ocean</a> and my Meterkast|<a href="http://blog.jasper.es/categories.html#lkml-ref">Read the blog</a></td><td>聽</td></tr></table><script language="javascript" src="/js/styleswitcher.js" type="text/javascript"></script></body></html>