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.
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.
Einzelheiten:
tsc
ist nicht in clocksource
registriert. Es sieht wie folgt aus.
$ cat /sys/devices/system/clocksource/clocksource0/available_clocksource
kvm-clock acpi_pm
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.
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.
$ 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
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.
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.
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
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.
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.
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.
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.
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.
$ 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
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.
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.
t1 = get_cycles ();
t2 = get_cycles ();
Zusammenfassend ist dieser Code auf folgende Weise problematisch:
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.
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.
Lassen Sie uns konkret überlegen, was mit den obigen Lösungen von 1 und 2 getan werden kann.
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
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
$ 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
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.
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
$ 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 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.
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;
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;
}
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