[LINUX] Ich kann die Uhrenquelle tsc nicht finden! ?? Die Geschichte des Versuchs, einen Kernel-Patch zu schreiben

Ich habe ein Problem, dass "tsc" in "clocksource" unter Linux unter KVM nicht erkannt werden kann, und ich habe versucht, es durch Ausprobieren zu beheben. Grundsätzlich habe ich versucht, einen benutzerdefinierten Patch auf die Distribution anzuwenden, aber es fühlte sich nicht richtig an. Danach habe ich den ursprünglichen Patch entdeckt und gelernt. Es ist eine Geschichte. Ich habe den gleichen Inhalt in meinem Blog gepostet, aber ich habe auch einen Artikel über Qiita geschrieben.

Hintergrund

Dies war vor mehr als einem Jahr, aber als ich mir die "Clocksource" von Linux ansah, die auf KVM läuft, bemerkte ich, dass das "tsc" möglicherweise nicht erkannt wird. Ist tsc das schnellste und genaueste für Linux-Gäste, die auf x86-Hypervisoren ausgeführt werden? Es ist ein Gegengerät, und in einigen Fällen können Probleme auftreten, wenn es nicht erkannt wird. Zu dieser Zeit las ich die Quelle des Zeitmanagements von x86 Linux und suchte nach Fehlern.

Spezifische Symptome

Einzelheiten: tsc ist nicht in clocksource registriert. Es sieht wie folgt aus.

$ cat /sys/devices/system/clocksource/clocksource0/available_clocksource 
kvm-clock acpi_pm 

Beispiele und Analogien

Das Betriebssystem, das ich damals verwendete, war der CentOS7-Kernel-Kernel-3.10.0-957.el7. Zunächst entschied ich mich, eine Analogie aus den Symptomen zu ziehen, um zu vergleichen, welche Art von Problem in welchem Teil mit dem Fall Xen liegt.

Beispiel: CentOS7 auf KVM

Das Folgende ist das Ergebnis der Bestätigung einschließlich der Ausgabe von dmesg.

$ cat /sys/devices/system/clocksource/clocksource0/available_clocksource 
kvm-clock acpi_pm 

$ dmesg | grep -iE '(clocksource|tsc)'
[    0.000000] tsc: Detected 3000.000 MHz processor
[    1.834422] TSC deadline timer enabled
[    2.600920] Switched to clocksource kvm-clock

Ich habe das Gefühl, dass der CentOS 7-Kernel im Vergleich zu Ubuntu usw. nichts ausgibt, aber ... Trotz der obigen Meldung wusste ich, dass TSC selbst als Gerät erkannt wurde, und ich war der Meinung, dass dies ein guter Hinweis sein könnte.

Danach bestätigte ich, dass es wichtig ist, es mit dem Normalfall zu vergleichen. Ich bin mir nicht sicher, ob es ein guter Vergleich ist, aber zum Glück wusste ich, dass ich diesmal kein Problem mit einem Linux-Gast haben würde, der auf Xen läuft, also habe ich es verglichen.

Vergleich mit Xen: CentOS7 unter Xen

$ cat /sys/devices/system/clocksource/clocksource0/available_clocksource 
xen tsc hpet acpi_pm 

$ dmesg | grep -iE '(clocksource|tsc)'
[    0.000000] tsc: Fast TSC calibration using PIT
[    0.000000] tsc: Detected 2394.486 MHz processor
[    0.031000] tsc: Detected 2394.476 MHz TSC
[    0.607022] Switched to clocksource xen
[    2.244080] tsc: Refined TSC clocksource calibration: 2394.455 MHz

Analogie

Obwohl es einen geringfügigen Unterschied bei der Erfassung der TSC-Frequenz gibt, können wir sehen, dass der Unterschied in der Anwesenheit oder Abwesenheit einer "verfeinerten TSC-Taktquellenkalibrierung" liegt. Natürlich ist es zu diesem Zeitpunkt strengstens verboten, Annahmen zu treffen, da dies eine grobe Vermutung ist.

Nach Meinung eines Amateurs ist es jedoch für diejenigen, die Probleme haben (Gäste, die mit KVM arbeiten) *, besonders ungewöhnlich, dass kein Fehler vorliegt und nichts ausgegeben wird. Ich kann mir vorstellen, dass. * Zumindest von hier aus

In der Erfahrung, normal mit Linux zu spielen? In solchen Fällen ist es bis zu einem gewissen Grad leicht, ein Gefühl dafür zu bekommen. Ich denke, es ist schneller, den Quellcode zu betrachten, als den Debugger (persönlich) zu verwenden. Es ist vielleicht auch keine sehr komplimentierte Methode, aber ich liebe es, sie mit printk-Debugging zu kombinieren.

Primitives Debuggen

Ich habe noch nie einen Kernel-Entwickler gekannt, und ich habe nur das Wissen, das ich aus Büchern, Zeitschriften und dem Internet gewonnen habe, aber ich finde, dass das "Printk" -Debugging auch ziemlich gut ist. Ich weiß nicht, wann es war, aber der Netfilter-Entwickler Rusty Russell hat in einer alten Zeitschrift einen Artikel gelesen, in dem "printk" und die Blendung in der Quellenliste das Problem lösen [1]. ..

[1] Sicherlich ein Open-Source-Magazin. Grundsätzlich stammt das meiste Wissen, das ich kenne, aus alten Zeitschriften und Büchern, die ich aus zweiter Hand gekauft habe.

Wenn Sie sich nicht in einer Situation befinden, in der Sie mit einem Funktionszeiger herumspringen oder mit einer komplizierten Verarbeitungslogik nachforschen müssen, werden Sie das Problem wahrscheinlich anhand des Quellcodes finden. Auch wenn dies nicht der Fall ist, kann die Verwendung eines Debuggers meiner Meinung nach hart sein.

Dieses Mal denke ich, dass es eine typische Lösung ist, die durch printk-Debugging gelöst werden kann.

Quellenvorbereitung

Installieren und bereiten Sie die Quelle usw. mit dem folgenden Befehl vor. Ich erinnere mich, dass ich diese Schritte auf der offiziellen Seite von CentOS ausgeführt habe.

$ sudo yumdownloader --source kernel-3.10.0-957.el7
$ sudo yum groupinstall "Development Tools" -y 
$ sudo yum install rpmdevtools -y
$ rpmdev-setuptree
$ rpm -Uvh kernel-3.10.0-957.el7.src.rpm
$ sudo yum-builddep -y --enablerepo=* rpmbuild/SPECS/kernel.spec
$ rpmbuild -bp ~/rpmbuild/SPECS/kernel.spec
$ cp -r ~/rpmbuild/BUILD/kernel-3.10.0-957.el7 ~/rpmbuild/BUILD/kernel-3.10.0-957.el7.orig
$ cp -al ~/rpmbuild/BUILD/kernel-3.10.0-957.el7.orig ~/rpmbuild/BUILD/kernel-3.10.0-957.el7.new

Da es sich bei der Bearbeitung mit vim um "cp-al" handelt, stellen Sie ein, dass die feste Verbindung zum Zeitpunkt des Schreibens unterbrochen wird. Zum Beispiel das folgende ".vimrc"

set nobackup
set writebackup
set backupcopy=no

Erstellen Sie ein Makro

Es ist mühsam, nacheinander über das Argument von "printk" nachzudenken, daher ist es natürlich, aber ich werde ein einfaches Makro erstellen. Zum Beispiel:

#define __dprintk(n) \
printk("No.%s, func: %s, line: %d, file: %s\n", \
#n, __FUNCTION__, __LINE__, __FILE__);

Der Funktionsname, die Zeilennummer und der Dateiname sowie die Nummer des eingefügten printk werden ausgegeben.

Wo sind die wahrscheinlichen Fehler?

Dies ist natürlich der Ort, an dem die "Clocksource tsc" registriert werden kann, da die Registrierung der "Clocksource" nicht funktioniert. Werfen wir einen Blick auf die verdächtigen Teile und darauf, wie Sie auf "printk" klicken.

Eingang

Grundsätzlich ist der Code für die TSC-Verarbeitung in / arch / x86 / kernel / tsc.c definiert, und clocksource ist bei der Funktion clocksource_registar_hogehoge registriert. Überprüfen Sie daher den Anrufort.

Wenn Sie richtig "grep", können Sie sehen, dass es wie folgt ist.

static int __init init_tsc_clocksource(void)
{
    if (!cpu_has_tsc || tsc_disabled > 0 || !tsc_khz)
        return 0;

    if (tsc_clocksource_reliable)
        clocksource_tsc.flags &= ~CLOCK_SOURCE_MUST_VERIFY;
    /* lower the rating if we already know its unstable: */
    if (check_tsc_unstable()) {
        clocksource_tsc.rating = 0;
        clocksource_tsc.flags &= ~CLOCK_SOURCE_IS_CONTINUOUS;
    }

    if (boot_cpu_has(X86_FEATURE_NONSTOP_TSC_S3))
        clocksource_tsc.flags |= CLOCK_SOURCE_SUSPEND_NONSTOP;

    /*
     * Trust the results of the earlier calibration on systems
     * exporting a reliable TSC.
     */
    if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE)) {
        clocksource_register_khz(&clocksource_tsc, tsc_khz);
        return 0;
    }

    schedule_delayed_work(&tsc_irqwork, 0);
    return 0;
}
/*
 * We use device_initcall here, to ensure we run after the hpet
 * is fully initialized, which may occur at fs_initcall time.
 */
device_initcall(init_tsc_clocksource);

Es gibt ein clocksource_register_khz. Es gibt auch "device_initcall". Ich denke das ist richtig.

Wenn Sie wissen, dass die TSC-Frequenz zuverlässig ist, können Sie "X86_FEATURE_TSC_RELIABLE" einstellen und "clocksource_tsc" schnell registrieren. Die TSC-Frequenz wird jedoch bei jedem Start berechnet, und normalerweise wissen Sie das nicht. Ich kann mir also vorstellen, dass Sie diesen If-Block nicht betreten können.

Ich finde das jedoch ursprünglich seltsam. Wenn Sie mit dem Mechanismus von "pvclock" vertraut sind, wissen Sie, dass die Frequenz vom Hypervisor auf der Seite mit den gemeinsam genutzten Informationen zugewiesen wird. Wenn Sie also "X86_FEATURE_TSC_RELIABLE" festlegen, ist nicht alles erledigt? Ich bemerke. In Anbetracht dessen, wie es aufgetreten ist, scheint dies nicht die Essenz dieses Problems zu sein, daher werde ich es verschieben.

Dann erreicht es das Ende der Funktion und es kann gefolgert werden, dass die folgende Verarbeitung das Problem ist.

schedule_delayed_work(&tsc_irqwork, 0);

Schedule_delayed_work ist eine häufig verwendete Funktion, die viele von Ihnen vielleicht kennen [2]. Es ist normalerweise in der Dokumentation enthalten, aber Sie können es erwarten, ohne die Dokumentation zu lesen. Sie können feststellen, dass Sie eine asynchrone Verarbeitung ausführen, indem Sie einen Zeiger auf eine bestimmte Verarbeitung und eine Verzögerungszeit übergeben.

[2] schedule_delayed_work

Wie man ein Zeichen setzt

Da sich in der Funktion init_tsc_clocksource ein Teil befindet, der scheinbar früh zurückkehrt, ohne einen Fehler auszugeben, nur für den Fall, dass überprüft wird, ob mit dem nächsten Prozess fortgefahren wird, der zuvor definierte __dprintk Ich werde schlagen. Ich weiß nicht, wie weit der Prozess geht, deshalb habe ich es so gemacht, um es später einfacher zu machen, der Kernel-Nachricht zu folgen.

static int __init init_tsc_clocksource(void)
{
    if (!cpu_has_tsc || tsc_disabled > 0 || !tsc_khz)
        return 0;
__dprintk(1);
    if (tsc_clocksource_reliable)
        clocksource_tsc.flags &= ~CLOCK_SOURCE_MUST_VERIFY;
    /* lower the rating if we already know its unstable: */
    if (check_tsc_unstable()) {
        clocksource_tsc.rating = 0;
        clocksource_tsc.flags &= ~CLOCK_SOURCE_IS_CONTINUOUS;
    }

    if (boot_cpu_has(X86_FEATURE_NONSTOP_TSC_S3))
        clocksource_tsc.flags |= CLOCK_SOURCE_SUSPEND_NONSTOP;

    /*
     * Trust the results of the earlier calibration on systems
     * exporting a reliable TSC.
     */
    if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE)) {
        clocksource_register_khz(&clocksource_tsc, tsc_khz);
        return 0;
    }
__dprintk(2);
    schedule_delayed_work(&tsc_irqwork, 0);
__dprintk(3);
    return 0;
}

Danach werde ich weiterhin die verdächtigen Teile betrachten, während ich "printk" tippe. Es sieht nicht gut aus (bitteres Lächeln). Da printk synchron ist, ist es auch keine gute Idee, es zu überschreiben, also ist es eine gute Idee. .. .. Wenn es zu weit draußen ist, wird es gedrosselt, oder umgekehrt ist dies wahrscheinlich ein Problem.

Verdächtiger Teil

Die Fortsetzung des Prozesses war "tsc_irqwork". Wenn Sie in der Quellliste nach seiner Identität suchen, werden Sie feststellen, dass es wie folgt ist.

static DECLARE_DELAYED_WORK(tsc_irqwork, tsc_refine_calibration_work);
static void tsc_refine_calibration_work(struct work_struct *work)
{
    static u64 tsc_start = -1, ref_start;
    static int hpet;
    u64 tsc_stop, ref_stop, delta;
    unsigned long freq;

    /* Don't bother refining TSC on unstable systems */
    if (check_tsc_unstable()) 	//Wenn TSC hier als betrügerisch eingestuft wird
        goto out; 	 	//Sie sollten eine Fehlermeldung erhalten, wenn Sie ausgehen
                     	 	//Dies scheint kein Problem zu sein, aber nur für den Fall__Hit dprintk
__dprintk(4);

    /*
     * Since the work is started early in boot, we may be
     * delayed the first time we expire. So set the workqueue
     * again once we know timers are working.
     */
    //Bis Sie bestätigen können, dass der Timer ordnungsgemäß funktioniert
    // schedule_delayed_Es scheint, dass die Arbeit versucht, denselben Prozess rekursiv aufzurufen.
    //Es sieht verdächtig aus, also drinnen__Hit dprintk
    if (tsc_start == -1) {
__dprintk(5);
        /*
         * Only set hpet once, to avoid mixing hardware
         * if the hpet becomes enabled later.
         */
        hpet = is_hpet_enabled();
        schedule_delayed_work(&tsc_irqwork, HZ); //Das sieht verdächtig aus.
        tsc_start = tsc_read_refs(&ref_start, hpet);
        return;
    }
__dprintk(6);

    //Wieder tsc_read_Refs sind out und Sie können sehen, dass diese Funktion wichtig ist
    tsc_stop = tsc_read_refs(&ref_stop, hpet);

    // ref_starten und ref_acpi für stop_Sie können sehen, dass der Wert von pm oder hpet gleich eingegeben wird.
    //Selbst in diesem Fall erhalten Sie eine Nachricht, wenn Sie ausgehen, sodass es keine Rolle zu spielen scheint.
    //Printk nur für den Fall
    //ACPI PM selbst ist auch in Nitro.
    /* hpet or pmtimer available ? */
    if (ref_start == ref_stop)
        goto out;
__dprintk(7);

    //Wenn Sie sich Folgendes ansehen, können Sie vorhersagen, dass eine Art Nachricht angezeigt wird, wenn Sie ausgehen.
    //Mach weiter mit dprintk hinter goto

    /* Check, whether the sampling was disturbed by an SMI */
    if (tsc_start == ULLONG_MAX || tsc_stop == ULLONG_MAX)
        goto out;
__dprintk(8);

    delta = tsc_stop - tsc_start;
    delta *= 1000000LL;
    if (hpet)
        freq = calc_hpet_ref(delta, ref_start, ref_stop);
    else
        freq = calc_pmtimer_ref(delta, ref_start, ref_stop);

    /* Make sure we're within 1% */
    if (abs(tsc_khz - freq) > tsc_khz/100)
        goto out;
__dprintk(9);
    tsc_khz = freq;
    //Es ist klar, dass wir diesen Punkt noch nicht erreicht haben
    pr_info("Refined TSC clocksource calibration: %lu.%03lu MHz\n",
        (unsigned long)tsc_khz / 1000,
        (unsigned long)tsc_khz % 1000);

out:
    if (boot_cpu_has(X86_FEATURE_ART))
        art_related_clocksource = &clocksource_tsc;
__dprintk(10);
    clocksource_register_khz(&clocksource_tsc, tsc_khz);
}

Anhand der obigen Informationen können Sie die Position vorhersagen, an der bis zu einem gewissen Grad ein Problem vorliegt. Lassen Sie uns jedoch überprüfen, was mit printk geschieht.

Versuchen Sie, mit dem oben genannten als Bestätigungs-Patch zu erstellen

$ cd ~/rpmbuild/BUILD
$ diff -uNrp kernel-3.10.0-957.el7.orig kernel-3.10.0-957.el7.new > ../SOURCES/linux-3.10.0-957.el7.patch 
$ cd ../SOURCES
$ (rm linux-3.10.0-957.el7.patch && sed 's/kernel-[^ ][^ ]*[gw]\/lin/lin/g' > linux-3.10.0-957.el7.patch) < linux-3.10.0-957.el7.patch
$ cd ../BUILD
$ cd ~/rpmbuild/BUILD/kernel-3.10.0-957.el7/linux-3.10.0-957.el7.x86_64/
$ cp /boot/config-3.10.0-957.el7.x86_64 .config
$ make oldconfig
$ cp .config ~/rpmbuild/SOURCES/config-`uname -m`-generic
$ cd ~/rpmbuild/SPECS
$ vim kernel.spec
$ cat kernel.spec | grep -E '(tscheck|ApplyOptionalPatch.*[3].*|Patch1000)'
%define buildid .tscheck
Patch1000: linux-3.10.0-957.el7.patch
ApplyOptionalPatch linux-3.10.0-957.el7.patch 

$ rpmbuild -bb --with baseonly --without debuginfo --without debug --without doc --without perf --without tools --without kdump --without bootwrapper --target=`uname -m` kernel.spec
$ sudo yum localinstall -y ~/rpmbuild/RPMS/x86_64/kernel-*.rpm

Die Kraft von printk

Es wurde wie folgt. Sie sehen auf einen Blick, wo ein Problem vorliegt.

$ dmesg | grep -iE '(clocksource|tsc)'
[    0.000000] Linux version 3.10.0-957.el7.tscheck.x86_64 ...
...
[    0.000000] tsc: Detected 3000.000 MHz processor
[    1.804286] TSC deadline timer enabled
[    2.557401] Switched to clocksource kvm-clock
[    3.035763] No.1, func: init_tsc_clocksource, line: 1309, file: arch/x86/kernel/tsc.c
[    3.044996] No.2, func: init_tsc_clocksource, line: 1330, file: arch/x86/kernel/tsc.c
[    3.054436] No.3, func: init_tsc_clocksource, line: 1332, file: arch/x86/kernel/tsc.c
[    3.063727] No.4, func: tsc_refine_calibration_work, line: 1248, file: arch/x86/kernel/tsc.c
[    3.073240] No.5, func: tsc_refine_calibration_work, line: 1256, file: arch/x86/kernel/tsc.c
[    4.083424] No.4, func: tsc_refine_calibration_work, line: 1248, file: arch/x86/kernel/tsc.c
[    4.092902] No.5, func: tsc_refine_calibration_work, line: 1256, file: arch/x86/kernel/tsc.c
[    5.085423] No.4, func: tsc_refine_calibration_work, line: 1248, file: arch/x86/kernel/tsc.c
[    5.085424] No.5, func: tsc_refine_calibration_work, line: 1256, file: arch/x86/kernel/tsc.c
...
[   76.453766] No.5, func: tsc_refine_calibration_work, line: 1256, file: arch/x86/kernel/tsc.c
[   77.464261] No.4, func: tsc_refine_calibration_work, line: 1248, file: arch/x86/kernel/tsc.c
[   77.473952] No.5, func: tsc_refine_calibration_work, line: 1256, file: arch/x86/kernel/tsc.c
[   78.484266] No.4, func: tsc_refine_calibration_work, line: 1248, file: arch/x86/kernel/tsc.c
[   78.494070] No.5, func: tsc_refine_calibration_work, line: 1256, file: arch/x86/kernel/tsc.c
...
[  627.100177] No.5, func: tsc_refine_calibration_work, line: 1256, file: arch/x86/kernel/tsc.c
[  628.110663] No.4, func: tsc_refine_calibration_work, line: 1248, file: arch/x86/kernel/tsc.c
[  628.120099] No.5, func: tsc_refine_calibration_work, line: 1256, file: arch/x86/kernel/tsc.c

Sie können sehen, dass es die Endungen Nr. 4 und Nr. 5 endlos durchläuft. Anscheinend können Sie sehen, dass es mit wiederholter Verarbeitung jede Sekunde endlos weitergeht.

Wie erwartet können wir daher den Schluss ziehen, dass es wahrscheinlich Probleme gibt mit:

if (tsc_start == -1) {
__dprintk(5);
    /*
     * Only set hpet once, to avoid mixing hardware
     * if the hpet becomes enabled later.
     */
    hpet = is_hpet_enabled();
    schedule_delayed_work(&tsc_irqwork, HZ); //Das sieht verdächtig aus.
    tsc_start = tsc_read_refs(&ref_start, hpet);
    return;
}

Vom Verarbeitungsinhalt ist der "tsc_start" des obigen Blocks immer "-1", und der rekursive Aufruf von "tsc_irqwork" ist endlos. Als Ergebnis können Sie sehen, dass der Prozess nicht fortgesetzt wurde und kein Fehler ausgegeben wurde. Es ist wahrscheinlicher, dass diese Art von Problem nicht verstanden wird, selbst wenn Sie den Debugger verwenden. Es scheint also, dass die Verwendung von "printk" diesmal nicht so schlimm ist.

Wann wird "tsc_start" zu "-1"? Sie können dies sehen, indem Sie "tsc_read_refs" lesen.

Problemanalyse

tsc_read_refs ist unten definiert

#define MAX_RETRIES     5
#define SMI_TRESHOLD    50000

/*
 * Read TSC and the reference counters. Take care of SMI disturbance
 */
static u64 tsc_read_refs(u64 *p, int hpet)
{
        u64 t1, t2;
        int i;

        for (i = 0; i < MAX_RETRIES; i++) {
                t1 = get_cycles();
                if (hpet)
                        *p = hpet_readl(HPET_COUNTER) & 0xFFFFFFFF;
                else
                        *p = acpi_pm_read_early();
                t2 = get_cycles();
                if ((t2 - t1) < SMI_TRESHOLD)
                        return t2;
        }
        return ULLONG_MAX;
}

Anscheinend wurde "ULONG_MAX" zurückgegeben, weil ich dachte, es sei "-1".

Die spezifische Verarbeitung scheint wie folgt zu sein.

  1. Lesen Sie zuerst den TSC-Zählwert mit t1 = get_cycles ();
  2. Lesen Sie als Nächstes den Zählwert von "hpet" oder "acpi_pm" durch E / A-Zugriff.
  3. Lesen Sie unmittelbar danach den TSC-Zählwert erneut mit t2 = get_cycles ();
  4. Gibt "t2" zurück, wenn die Anzahl der während des E / A-Zugriffs verstrichenen Zählungen geringer als "SMI_TRESHOLD" ist.
  5. Andernfalls wird "ULLONG_MAX" zurückgegeben.

Zusammenfassend ist dieser Code auf folgende Weise problematisch:

Die Ursache finden

Wenn die TSC-Zählgeschwindigkeit relativ hoch ist (oder die Eingabe / Ausgabe an ACPI_PM oder den HPET-Timer relativ langsam ist) Der Unterschied zwischen "t1 = get_cycles ()" und "t2 = get_cycles ()" ist zu groß und "t2 - t1" ist immer größer als "SMI_TRESHOLD". Infolgedessen ist "tsc_start" immer "-1" und versucht es auch nach Beendigung des Startvorgangs erneut mit "Schedule_delayed_wor" (& tsc_irqwork, HZ) ". Schließlich wird die Initialisierung der TSC-Taktquelle endlos verzögert, und die Taktquelle "tsc" fehlt endlos. Und es ist schwer zu sagen, was los ist, da bei diesem Wiederholungsvorgang keine Meldung angezeigt wird.

Schreibe einen Patch

Ich habe einen Patch geschrieben, der nur ein "printk" zum Debuggen einfügt, aber ich habe bereits einen Patch angewendet, der das Problem tatsächlich behebt. Ob es richtig oder falsch ist ...

Obwohl es sich um eine Amateuridee handelt, gibt es zwei Möglichkeiten, dieses Problem zu beheben.

  1. Es ist etwas seltsam, dass "SMI_TRESHOLD" ein fester Wert ist, da es möglich ist, dass die Geschwindigkeit von TSC und die Latenz von IO je nach Gerät und Umgebung zunimmt. Ich denke, es gibt einen guten Weg, dies zu tun.
  2. Wie eingangs erwähnt, ist die Frequenz der TSC im Voraus in "pvclock" bekannt. Wenn Sie es verwenden, sollten Sie frei von störenden Dingen sein.

Lassen Sie uns konkret überlegen, was mit den obigen Lösungen von 1 und 2 getan werden kann.

Lösung 1: Es ist etwas seltsam, dass SMI_TRESHOLD ein fester Wert ist.

Es scheint ein wenig seltsam, dass "SMI_TRESHOLD" ein fester Wert ist. Ich kenne die Tiefe des Linux-Kernels nicht, aber aus Sicht eines Amateurs handelt es sich hierbei um einen ziemlich umständlichen Prozess, und ich dachte, es sollte möglich sein, bis zu einem gewissen Grad je nach System zu skalieren. Vielleicht sollte der Wert proportional zur Frequenz des TSC sein. .. .. Aber ehrlich gesagt wusste ich nicht wie viel.

(Antwort in einem Zustand des Hirntodes gegeben, weil es ein Feiertag von nur einem Tag ist)

"Machen wir einen größeren festen Wert!"

(Großes Problem, aber ich weiß nicht, was das Problem ist)

50000 -> 5000000

Ich habe den folgenden Patch geschrieben und ihn gesehen. Vol 1

diff -uNrp linux-3.10.0-957.el7.x86_64/arch/x86/kernel/tsc.c linux-3.10.0-957.el7.x86_64/arch/x86/kernel/tsc.c
--- linux-3.10.0-957.el7.x86_64/arch/x86/kernel/tsc.c    2019-07-28 18:54:36.422551294 +0000
+++ linux-3.10.0-957.el7.x86_64/arch/x86/kernel/tsc.c    2019-07-28 18:55:24.100351452 +0000
@@ -391,7 +391,7 @@ static int __init tsc_setup(char *str)
 __setup("tsc=", tsc_setup);
 
 #define MAX_RETRIES     5
-#define SMI_TRESHOLD    50000
+#define SMI_TRESHOLD    5000000
 
 /*
  * Read TSC and the reference counters. Take care of SMI disturbance

Es ist repariert. Vol 1

$  cat /sys/devices/system/clocksource/clocksource0/available_clocksource 
kvm-clock tsc acpi_pm 

$ dmesg | grep -iE '(clocksource|tsc)'
[    0.000000] tsc: Detected 3000.000 MHz processor
[    1.835560] TSC deadline timer enabled
[    2.605330] Switched to clocksource kvm-clock
[    3.086972] No.1, func: init_tsc_clocksource, line: 1309, file: arch/x86/kernel/tsc.c
[    3.096286] No.2, func: init_tsc_clocksource, line: 1330, file: arch/x86/kernel/tsc.c
[    3.105617] No.3, func: init_tsc_clocksource, line: 1332, file: arch/x86/kernel/tsc.c
[    3.114963] No.4, func: tsc_refine_calibration_work, line: 1248, file: arch/x86/kernel/tsc.c
[    3.124533] No.5, func: tsc_refine_calibration_work, line: 1256, file: arch/x86/kernel/tsc.c
[    4.209357] No.4, func: tsc_refine_calibration_work, line: 1248, file: arch/x86/kernel/tsc.c
[    4.219336] No.6, func: tsc_refine_calibration_work, line: 1266, file: arch/x86/kernel/tsc.c
[    4.229233] No.7, func: tsc_refine_calibration_work, line: 1273, file: arch/x86/kernel/tsc.c
[    4.239024] No.8, func: tsc_refine_calibration_work, line: 1278, file: arch/x86/kernel/tsc.c
[    4.248844] No.9, func: tsc_refine_calibration_work, line: 1290, file: arch/x86/kernel/tsc.c
[    4.258687] tsc: Refined TSC clocksource calibration: 3000.004 MHz
[    4.264562] No.10, func: tsc_refine_calibration_work, line: 1300, file: arch/x86/kernel/tsc.c

Lösung 2: Es ist etwas seltsam, die pvclock TSC-Frequenz nicht zu verwenden, obwohl es sich um einen virtualisierten Gast handelt

Wie ich zu Beginn sagte, wenn Sie mit dem Mechanismus von "pvclock" vertraut sind, wissen Sie, dass die Frequenz von der Karte auf der Seite mit den gemeinsam genutzten Informationen zugewiesen wird, sodass Sie TSC erkannt haben (erkennen Sie "kvm-clock"). Zu diesem Zeitpunkt ist bekannt, dass die TSC-Frequenz wie von der Karte berechnet angegeben wird. Wenn Sie also "X86_FEATURE_TSC_RELIABLE" setzen, wenn Sie "kvm-clock" erkennen, ist dann nicht alles erledigt? Ich bemerke. pvlock ist ein Protokoll, das TSC in erster Linie auf eine virtuelle Umgebung erweitert.

Es ist der folgende Teil.

/*
 * Trust the results of the earlier calibration on systems
 * exporting a reliable TSC.
 */
if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE)) {
    clocksource_register_khz(&clocksource_tsc, tsc_khz);
    return 0;
}

Es sollte einen Prozess geben, um die TSC-Frequenz zu erkennen, wenn der kvm-Takt erkannt wird Lassen Sie uns den Code von kvm-clock so ändern, dass er im obigen Teil in if eingefügt werden kann.

Die Quelle des Ziels "kvm-clock" ist wie folgt. Sie können sehen, dass "kvm_get_tsc_khz" während des Gastinitialisierungsprozesses aufgerufen wird, der auf KVM ausgeführt wird. Aus den Kommentaren im Quellcode können wir irgendwie verstehen, dass die TSC-Frequenz hier früh berechnet wird.

static unsigned long kvm_get_tsc_khz(void)
{
        struct pvclock_vcpu_time_info *src;
        int cpu;
        unsigned long tsc_khz;
   ...
        src = &hv_clock[cpu].pvti;
        tsc_khz = pvclock_tsc_khz(src);
        preempt_enable();
        return tsc_khz;
}
...
void __init kvmclock_init(void)
{
    ...
        x86_platform.calibrate_tsc = kvm_get_tsc_khz;
        x86_platform.calibrate_cpu = kvm_get_tsc_khz;
...

Wenn Sie ein Linux-Benutzer sind, wissen Sie wahrscheinlich, dass CPU-Flags von den Makros arch / x86 / kernel / cpu / mkcapflags.pl und arch / x86 / include / asm / cpufeature.h verwaltet werden. Als ich nach etwas suchte, das die Flagge erzwang, fand ich Folgendes: arch / x86 / include / asm / cpufeature.h.

#define set_cpu_cap(c, bit)    set_bit(bit, (unsigned long *)((c)->x86_capability))

extern void setup_clear_cpu_cap(unsigned int bit);
extern void clear_cpu_cap(struct cpuinfo_x86 *c, unsigned int bit);

#define setup_force_cpu_cap(bit) do { \
    set_cpu_cap(&boot_cpu_data, bit);    \
    set_bit(bit, (unsigned long *)cpu_caps_set);    \
} while (0)

Benutze das.

Ich habe den folgenden Patch geschrieben und ihn gesehen. vol.2

diff -uNrp linux-3.10.0-957.el7.x86_64/arch/x86/kernel/kvmclock.c linux-3.10.0-957.el7.x86_64/arch/x86/kernel/kvmclock.c
--- linux-3.10.0-957.el7.x86_64/arch/x86/kernel/kvmclock.c    2019-07-29 02:35:27.318987845 +0000
+++ linux-3.10.0-957.el7.x86_64/arch/x86/kernel/kvmclock.c    2019-07-29 03:04:11.015862936 +0000
@@ -338,6 +338,7 @@ void __init kvmclock_init(void)
 
     x86_platform.calibrate_tsc = kvm_get_tsc_khz;
     x86_platform.calibrate_cpu = kvm_get_tsc_khz;
+    setup_force_cpu_cap(X86_FEATURE_TSC_RELIABLE);
     x86_platform.get_wallclock = kvm_get_wallclock;
     x86_platform.set_wallclock = kvm_set_wallclock;
 #ifdef CONFIG_X86_LOCAL_APIC

Es ist repariert. vol.2

$ cat /sys/devices/system/clocksource/clocksource0/available_clocksource 
kvm-clock tsc acpi_pm 

$  dmesg | grep -iE '(clocksource|tsc)'
[    0.000000] Linux version 3.10.0-957.21.3.el7.tsc_fixed.x86_64...
...
[    0.000000] tsc: Detected 3000.000 MHz processor
[    1.832686] TSC deadline timer enabled
[    1.929653] Skipped synchronization checks as TSC is reliable.
[    2.598602] Switched to clocksource kvm-clock
[    3.078334] No.1, func: init_tsc_clocksource, line: 1309, file: arch/x86/kernel/tsc.c

Der Patch kann nicht geschrieben werden!

Der in CentOS / RHEL verwendete Kernel ist etwas alt. Um ehrlich zu sein, denke ich, dass die meisten Fehler, die Sie in Ihrer Distribution finden, bereits im Upstream behoben wurden. Als ich danach suchte, fand ich es leicht, orz.

Besserer Patch als Lösung 1

Wie erwartet wurde es modifiziert, um proportional zur TSC-Frequenz zu skalieren. Die Methode zum Vergleichen von "ULONG_MAX" wurde ebenfalls festgelegt.

Ich habe verstanden, dass es verschoben werden sollte, wenn es proportional zu einem bestimmten Wert ist (nicht. Ich bin mir über die Details nicht sicher, aber ich dachte, es wäre ein Patch wie die Essenz von CS. Ich habe viel über diese Art von Gefühl gelernt, deshalb möchte ich es beim nächsten Mal nutzen.

x86/tsc: Make calibration refinement more robust

Auszug nur für charakteristische Teile

diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
index e9f777b..3fae238 100644
--- a/arch/x86/kernel/tsc.c
+++ b/arch/x86/kernel/tsc.c
@@ -297,15 +297,16 @@ static int __init tsc_setup(char *str)
 
 __setup("tsc=", tsc_setup);
 
-#define MAX_RETRIES     5
-#define SMI_TRESHOLD    50000
+#define MAX_RETRIES		5
+#define TSC_DEFAULT_THRESHOLD	0x20000
 
 /*
- * Read TSC and the reference counters. Take care of SMI disturbance
+ * Read TSC and the reference counters. Take care of any disturbances
  */
 static u64 tsc_read_refs(u64 *p, int hpet)
 {
 	u64 t1, t2;
+	u64 thresh = tsc_khz ? tsc_khz >> 5 : TSC_DEFAULT_THRESHOLD;
 	int i;
 
 	for (i = 0; i < MAX_RETRIES; i++) {
@@ -315,7 +316,7 @@ static u64 tsc_read_refs(u64 *p, int hpet)
 		else
 			*p = acpi_pm_read_early();
 		t2 = get_cycles();
-		if ((t2 - t1) < SMI_TRESHOLD)
+		if ((t2 - t1) < thresh)
 			return t2;
 	}
 	return ULLONG_MAX;

Patch ähnlich Lösung 2

Der RHEL-Code ist etwas alt, daher gibt es einen kleinen Unterschied in den von Ihnen gesetzten CPU-Flags. Um ehrlich zu sein, habe ich die Bedeutung des Einfügens von "setup_force_cpu_cap" in diesen Block nicht ganz verstanden. Dieser Code muss das CPU-Flag viele Male zurücksetzen. Ich frage mich, was es bedeutet, aber es kann etwas zu tun haben.

kvmclock: fix TSC calibration for nested guests

diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c
index d79a18b..4c53d12 100644
--- a/arch/x86/kernel/kvmclock.c
+++ b/arch/x86/kernel/kvmclock.c
@@ -138,6 +138,7 @@ static unsigned long kvm_get_tsc_khz(void)
 	src = &hv_clock[cpu].pvti;
 	tsc_khz = pvclock_tsc_khz(src);
 	put_cpu();
+	setup_force_cpu_cap(X86_FEATURE_TSC_KNOWN_FREQ);
 	return tsc_khz;
 }

Nachwort

Linux ist schwierig, macht aber Spaß. Übrigens, ich liebe Rust und schreibe sogar einen Bootloader für Linux. Dies ist auch ein Einführungsartikel. Bitte lesen Sie ihn, wenn Sie möchten.

Eine Geschichte über die Erstellung eines x86-Bootloaders, der vmlinux mit Rust - Qiita booten kann

Bitte zögern Sie nicht zu kommentieren, wenn Sie einen solchen Rat haben, ist dies keine gute Idee. Wird lernen!

Recommended Posts

Ich kann die Uhrenquelle tsc nicht finden! ?? Die Geschichte des Versuchs, einen Kernel-Patch zu schreiben
Die Geschichte des Versuchs, den Client wieder zu verbinden
Die Geschichte der IPv6-Adresse, die ich auf ein Minimum beschränken möchte
Die Geschichte eines hochrangigen Technikers, der versucht, das Überleben der Titanic vorherzusagen
Eine Geschichte über den Versuch, Linter mitten in einem Python (Flask) -Projekt vorzustellen
Ich habe versucht, mit TensorFlow den Durchschnitt mehrerer Spalten zu ermitteln
Schreiben Sie ein Python-Programm, um die Bearbeitungsentfernung [Python] [Levenshtein-Entfernung] zu ermitteln.
Ich habe eine Funktion erstellt, um das Modell von DCGAN zu überprüfen
So ermitteln Sie den Skalierungskoeffizienten eines bipolaren Wavelets
Ich möchte den Schnittpunkt einer Bezier-Kurve und einer geraden Linie finden (Bezier-Clipping-Methode)
[Einführung in StyleGAN] Ich habe mit "The Life of a Man" ♬ gespielt
Ich habe den Code geschrieben, um den Brainf * ck-Code in Python zu schreiben
So ermitteln Sie die Speicheradresse des Pandas-Datenrahmenwerts
Eine Geschichte, in der der Algorithmus zu einem lächerlichen Ergebnis kam, als er versuchte, das Problem der reisenden Verkäufer richtig zu lösen
Die Geschichte des Exportierens eines Programms
[Python] Eine einfache Funktion zum Ermitteln der Mittelkoordinaten eines Kreises
Eine Geschichte über den Versuch, den Testprozess eines 20 Jahre alten Systems in C zu verbessern
Ich habe versucht, den besten Weg zu finden, um einen guten Ehepartner zu finden
zoom Ich habe versucht, den Grad der Aufregung der Geschichte auf der Konferenz zu quantifizieren
Eine Geschichte, die mich süchtig nach dem Versuch machte, LightFM unter Amazon Linux zu installieren
Eine Geschichte, die ich süchtig danach war, eine Video-URL mit Tweepy zu bekommen
Ein Memorandum darüber, wie man Pandas schreibt, das ich persönlich oft vergesse
Ich möchte eine Liste in der Reihenfolge anderer Listen sortieren
Eine Geschichte über einen Anfänger im Deep Learning, der versucht, Gitarren mit CNN zu klassifizieren
Ich habe versucht, die optimale Route des Traumlandes durch (Quanten-) Tempern zu finden
Ich habe versucht, die Phase der Geschichte mit COTOHA zu extrahieren und zu veranschaulichen
Ich habe ein Programm erstellt, um die Größe einer Datei mit Python zu überprüfen
Ich habe versucht, den Höhenwert von DTM in einem Diagramm anzuzeigen
Ich habe die übliche Geschichte ausprobiert, Deep Learning zu verwenden, um den Nikkei-Durchschnitt vorherzusagen
Ich habe versucht, das Ergebnis des A / B-Tests mit dem Chi-Quadrat-Test zu überprüfen
Die Geschichte des Versuchs, SSH_AUTH_SOCK mit LD_PRELOAD auf dem Bildschirm veraltet zu halten
Python: Ich möchte die Verarbeitungszeit einer Funktion genau messen
Ich habe eine Funktion erstellt, um die Bewegung eines zweidimensionalen Arrays (Python) zu sehen.
Machen wir einen Jupyter-Kernel
Die Geschichte, MeCab in Ubuntu 16.04 zu setzen
Die Geschichte, deep3d auszuprobieren und zu verlieren
Die Geschichte der Verarbeitung A von Blackjack (Python)
Die Geschichte von pep8 wechselt zu pycodestyle
Ich habe ein Tool erstellt, um die Ausführungszeit von cron zu schätzen (+ PyPI-Debüt)
Fühlen Sie sich frei, einen Test mit der Nase zu schreiben (im Fall von + gevent)
[Circuit x Python] So ermitteln Sie die Übertragungsfunktion eines Schaltkreises mit Lcapy
Ein Programmieranfänger versuchte, die Ausführungszeit des Sortierens usw. zu überprüfen.
Beachten Sie die Lösung, da Django nicht mit pip installiert werden konnte
Ich habe einen Appdo-Befehl erstellt, um Befehle im Kontext der App auszuführen
Ich möchte einen Lebenszyklus in der Aufgabendefinition von ECS festlegen
Ich möchte dem Anfang einer WAV-Datei 1 Sekunde lang Stille hinzufügen
Ich möchte eine Liste der WebDAV-Dateien im Modul Anfragen anzeigen
Ich habe ein Tool zum automatischen Sichern der Metadaten der Salesforce-Organisation erstellt
[GAN] Ich sah die Dunkelheit versuchen, die endgültige Entwicklung von Pokemon weiterzuentwickeln
Ich habe ein Skript erstellt, das das aktive Fenster mit win32gui von Python aufzeichnet
Die Geschichte von soracom_exporter (Ich habe versucht, SORACOM Air mit Prometheus zu überwachen)
Ich musste im Unterricht keinen Dekorateur schreiben. Danke Kontextmanager
Ich habe versucht, ein Modell mit dem Beispiel von Amazon SageMaker Autopilot zu erstellen
So berechnen Sie die Volatilität einer Marke
So finden Sie den Bereich des Boronoi-Diagramms
Die Hand von "Millijan" durch Kombinationsoptimierung finden
Ich habe versucht, das Umfangsverhältnis mit 100 Millionen Stellen zu ermitteln