2011-12-02 17 views
6

Ho un driver che richiede ritardi al microsecondo. Per creare questo ritardo, il mio driver sta usando la funzione udelay del kernel. Nello specifico, c'è una chiamata a udelay (90):Kernel Linux: udelay() restituisce troppo presto?

iowrite32(data, addr + DATA_OFFSET); 
iowrite32(trig, addr + CONTROL_OFFSET); 

udelay(30); 

trig |= 1; 
iowrite32(trig, addr + CONTROL_OFFSET); 

udelay(90); // This is the problematic call 

Abbiamo riscontrato problemi di affidabilità con il dispositivo. Dopo un sacco di debug, abbiamo rintracciato il problema alla ripresa del driver prima che sia passato 90 secondi. (Vedere "prova" di seguito.)

Sto eseguendo la versione del kernel 2.6.38-11 SMP generico (Kubuntu 11.04, x86_64) su un Intel Pentium Dual Core (E5700).

Per quanto ne so, la documentazione afferma che udelay ritarderà l'esecuzione di almeno il ritardo specificato ed è ininterrotta. C'è un bug in questa versione del kernel o ho frainteso qualcosa sull'uso di udelay?


Per convincerci che il problema è stato causato da udelay ritorno troppo presto, abbiamo alimentato un orologio 100kHz a una delle porte di I/O e implementato il nostro ritardo come segue:

// Wait until n number of falling edges 
// are observed 
void clk100_delay(void *addr, u32 n) { 
    int i; 

    for (i = 0; i < n; i++) { 
     u32 prev_clk = ioread32(addr); 
     while (1) { 
      u32 clk = ioread32(addr); 
      if (prev_clk && !clk) { 
       break; 
      } else { 
       prev_clk = clk; 
      } 
     } 
    } 
} 

... e l'autista ora funziona perfettamente.


Come nota finale, ho trovato a discussion che indica che l'adattamento della frequenza potrebbe essere la causa della famiglia * ritardo() di funzioni a comportarsi male, ma questo era su un braccio mailing list - ho ammesso questi problemi sarebbe non- esistente su un PC Linux x86.

risposta

2

E5700 ha X86_FEATURE_CONSTANT_TSC ma non X86_FEATURE_NONSTOP_TSC. Il TSC è la probabile sorgente di clock per lo udelay. A meno che non sia associato a uno dei core con una maschera di affinità, il tuo compito potrebbe essere stato preventivato e ripianificato su un'altra CPU durante lo udelay. Oppure il TSC potrebbe non essere stabile durante le modalità di CPU a potenza più bassa.

Si può provare a disabilitare gli interrupt o disabilitare la prelazione durante il udelay? Inoltre, prova a leggere il TSC prima e dopo.

+0

Lo proverò e tornerò da te. Solo così ho capito bene, ogni core ha il suo TSC, quindi se il processo che il mio driver sta servendo è stato ripianificato su un'altra CPU, il TSC potrebbe non essere lo stesso? Inoltre, ho capito correttamente che X86_FEATURE_CONSTANT_TSC significa che il TSC della CPU è stabile indipendentemente dal ridimensionamento della frequenza e X86_FEATURE_NONSTOP_TSC significa che il TSC non smetterà mai di contare? Se è così, quando si fermerebbe una CPU è TSC? –

+0

Il TSC può arrestarsi durante alcuni [Stati C] (http://en.wikipedia.org/wiki/Advanced_Configuration_and_Power_Interface#Processor_states). –

+2

Il [codice di ritardo basato su TSC] (http://lxr.linux.no/#linux+v2.6.38/arch/x86/lib/delay.c#L51) rappresenta correttamente lo spostamento tra le CPU durante il ritardo. L'interruzione del TSC durante il ritardo non farebbe che ritardare * più a lungo *, non * più corto *, quindi neanche questo è il problema. – caf

3

Non conosco alcun bug in quella versione del kernel (ma ciò non significa che non ce ne sia uno).

udelay() non è "uninterruptible" - non disabilita la prelazione, quindi il task può essere preimpostato da un task RT durante il ritardo. Tuttavia, lo stesso vale per l'implementazione del ritardo alternativo, quindi è improbabile che questo sia il problema.

Il tuo problema potrebbe essere un problema di coerenza DMA/ordine di memoria? L'implementazione del ritardo alternativo accede al bus, quindi questo potrebbe nascondere il problema reale come un effetto collaterale.

+0

Mi spiace, quello che intendevo per "non interrompibile" era che la chiamata dovrebbe essere restituita solo quando il ritardo è scaduto. In questo driver, non sto utilizzando DMA. Per quanto riguarda i problemi di coerenza della cache, il dispositivo è un FPGA PCI-Express. Durante il rilevamento del dispositivo, richiedo la sua area I/O e utilizzo ioremap_nocache() per ottenere l'indirizzo di base utilizzato in tutte le funzioni I/O. Pensavo che l'utilizzo di ioremap_nocache() assicurasse la coerenza della cache? –

Problemi correlati