Warum aktiviert der 8250 UART-Treiber TTY nicht, wenn mehr als 256 Zeichen anstehen?

8

Was ist die Motivation für diese Wenn-Bedingung void serial8250_tx_chars(struct uart_8250_port *up)?

if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
    uart_write_wakeup(port);

Es ist seit Linux 1.1.13 (Mai 1994) vorhanden und wird in den meisten UART-Treibern wiederholt.

Hintergrund: angepasstes Linux 3.4.91, eingebettetes System auf ARMv7, UART-Port 0 ist für 38400 Baud konfiguriert, 16-Byte-FIFO für E / A. Nichts davon kann in unserem Setup geändert werden.

Wenn printf-ing sehr stark von der Konsole über UART, der internen 4kB-Puffer ( UART_XMIT_SIZE) auffüllt und dann abgewürgt den Anwenderraum Prozess , bis der Puffer geleert wird (die bei 38400 Baud 1 Sekunde dauert!). Dann wiederholt sich dieses Verhalten. Dies liegt daran, dass die Funktion n_tty_write()in den Ruhezustand wechselt, wenn der Puffer voll ist, und aufgrund des oben genannten fragwürdigen Zustands nicht lange geweckt wird.

Ich würde es natürlicher und effizienter finden, wenn dieser Scheck einfach entfernt würde. Dann würden die printfs den Puffer so schnell wie möglich füllen und dann mit der Geschwindigkeit fortfahren, mit der der Puffer geleert wird , anstatt mit der Burst-Verarbeitung, die ich beobachte.

Es funktioniert gut in meiner Umgebung, aber ich vermisse oder missverstehe sicherlich etwas. Es muss einen Grund für die aktuelle Implementierung geben. Gibt es irgendwelche Nebenwirkungen, wenn ich diesen Zustand entferne?

Als Nebenfrage: Gibt es Konfigurationsoptionen, um dieses Verhalten zu optimieren, z. B. dass printf immer sofort zurückkehrt und die Ausgabe verwirft, wenn der Puffer voll ist?

Hans W. Heckel
quelle
Ohne zu viel darüber zu wissen, ist meine Vermutung folgende: Dies ist ein gewöhnliches serielles Linux-Konsolen-Setup. Ich habe mit denen auf der Standard-x86-Hardware gearbeitet. Für direkte serielle Verbindungen musste ich immer die Hardware-Flusskontrolle verwenden. Könnte eine Idee sein.
Vasquez
1
Es gibt wahrscheinlich einen Effizienzunterschied. Warum setzen Sie WAKEUP_CHARS nicht einfach auf UART_XMIT_SIZE - 128?
James Youngman
@ JamesYoungman Genau das habe ich auch getan. Prost!
Hans W. Heckel

Antworten:

2

Es ist eine Effizienzmaßnahme. Die CPU läuft so viel schneller als die serielle Schnittstelle, dass, wenn der Kernel den Userspace-Prozess jedes Mal ausführen lässt, wenn ein wenig Platz im Puffer vorhanden ist, für jedes einzelne Datenbyte eine Reise in den Userspace und zurück erfolgt. Das verschwendet sehr viel CPU-Zeit:

$ time dd if=/dev/zero of=/dev/null bs=1 count=10000000
10000000+0 records in
10000000+0 records out
10000000 bytes (10 MB, 9.5 MiB) copied, 5.95145 s, 1.7 MB/s

real    0m5.954s
user    0m1.960s
sys     0m3.992s

$ time dd if=/dev/zero of=/dev/null bs=1000 count=10000
10000+0 records in
10000+0 records out
10000000 bytes (10 MB, 9.5 MiB) copied, 0.011041 s, 906 MB/s

real    0m0.014s
user    0m0.000s
sys     0m0.012s

Der obige Test liest und schreibt nicht einmal ein echtes Gerät: Der gesamte Zeitunterschied besteht darin, wie oft das System zwischen Benutzerbereich und Kernelbereich wechselt.

Wenn der Benutzerbereich nicht aufgehalten werden soll, kann er nicht blockierende E / A verwenden oder mithilfe eines select()Anrufs überprüfen , ob Platz zum Schreiben auf das Gerät vorhanden ist. Wenn dies nicht der Fall ist, kann er die Speicherausgabe sichern Rest in einen eigenen Puffer und Fortsetzung der Verarbeitung. Zugegeben, das macht die Sache komplizierter, da Sie jetzt einen Puffer haben, den Sie leeren müssen ... aber wenn Sie stdio verwenden, ist das im Allgemeinen sowieso wahr.

Jander
quelle
Dies unterstützt den obigen Kommentar von @James zur Effizienz. Jetzt macht es für mich viel mehr Sinn. Danke auch für die Zahlen!
Hans W. Heckel
Es ist zu beachten, dass diese Frage seit Kernel 2.6 auf Code verweist, der häufig in Treibern der unteren Schicht (z. B. einem UART-Treiber) vorkommt. Diese Treiber bieten der höheren Ebene serial_core die Möglichkeit, mit Hardware zu interagieren. Es ist der serielle Kern, der tatsächlich mit dem Benutzerraum interagiert (es sei denn, der Hardwaretreiber implementiert seine eigenen Ioctls oder ähnliches). Ich glaube, die Antwort der Befragten gilt immer noch.
Andrew Falanga