Facoltà di Ingegneria - Dessert Research Group, Federico II … · 2018. 3. 12. · Facoltà di...
Transcript of Facoltà di Ingegneria - Dessert Research Group, Federico II … · 2018. 3. 12. · Facoltà di...
Facoltà di Ingegneria Corso di Studi in Ingegneria Informatica tesi di laurea magistrale
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen Anno Accademico 2011-2012 relatori Ch.mo prof. Marcello Cinque Ch.mo Ing. Roberto Natella Candidato Camillo D’Avino matr. M63/76
Alla mia famiglia
III
Indice
Abstract 5
Introduzione 8
Capitolo 1. Fault injection e robustness testing 11
1.1 Affidabilità dei sistemi software 11
1.2 Minacce e approcci all’affidabilità 14
1.3 Fault injection 15
1.4 Tipi di fault injection 17
1.4.1 Mutazione del codice 18
1.4.2 Robustness testing 22
1.5 Scopo della tesi 24
1.6 Struttura della tesi 26
Capitolo 2. Infrastrutture di virtualizzazione 27
2.1 Che cos’è la virtualizzazione? 28
2.1.1 Perché virtualizzare? 28
2.1.2 Macchina virtuale 29
2.1.3 Cenni storici 30
2.1.4 Alcuni esempi di virtualizzazione 31
2.1.5 Tipi di virtualizzazione 34
2.2 Xen hypervisor 39
2.2.1 Architettura 39
2.2.2 Configurazioni 43
2.2.3 Xen e i meccanismi di protezione dell’architettura IA-32 45
2.2.4 Gestione della memoria 47
2.2.5 Analogie con i sistemi operativi 49
2.2.6 Approfondimento sulle hypercall 56
Capitolo 3. Tool di fault injetcion 64
3.1 Scenario di riferimento 64
3.2 Architettura 66
3.3 Profiling 67
3.4 Scelta dei fault 67
3.5 Raccolta dei risultati 73
IV
3.6 Dettagli implementativi 73
3.6.1 Modifiche al codice sorgente 74
3.6.2 Modulo per l’iniezione di guasti 82
3.6.3 Iniettore DomU 84
Capitolo 4. Sperimentazioni 88
4.1 Profiling 88
4.2 Iniezione dei guasti 89
4.3 Scelta delle hypercall 94
4.4 Raccolta dei risultati 95
4.4.1 Risultati esperimento 1 95
4.4.2 Risultati esperimento 2 110
Bibliografia 116
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
5
Abstract
This paper aims to introduce a new approach to evaluate system reliability in
virtualized environments. The reference scenario includes a Xen hypervisor on
which resides several virtual machines, each equipped with a Unix like Operating
System. In particular, this work is intended to produce three results: a fault
injection tool in virtualized environments, an experiment to evaluate reliability
degree exhibited by a single system installed in the context just mentioned and
another experiment aimed to assess the obtainable isolation level in virtualized
environments. These two tests are made possible by the tools introduced in the
previous period.
The reasons of this work are based on a wide band of case studies in the
literature, where bugs in software systems have been, in extreme cases, even
fatal to humans. The main reason is that computer systems have found a place in
any context, critical and not. This condition stimulates the research to produce the
most effective solutions that increase system reliability. As mentioned before,
unlike many approaches in the literature, the novelty introduced by this, involves
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
6
fault injection in the software layer that provides the sharing of resources between
virtual machines, and not in the single virtual machine. Therefore, we don’t want
only test a single machine behavior but we want to extend the analysis to other
virtual machines without neglecting the entity that holds them together: the
hypervisor.
The second experiment also wants to lead a performance analysis.
The approach we have used for both tests is based on an existing method in the
literature: Ballista. This mechanism allows us to assess the robustness of a
system without knowing any implementation details. Indeed, the module
considered in the tests is represented by a generic function, to which, applied
input values are valid and not, in order to assess module reaction with inputs not
necessarily lawful.
This paper consists of four chapters.
The first chapter contains a discussion about the state of the art about system
reliability theory, emphasizing some possible approaches. In particular, we will
discuss the method on which is based the tool: Ballista. Finally, we report a brief
description of the test scenario, so that the reader has an overview of what will be
shown in later chapters.
The second chapter consists of a broad discussion about the virtualization theory,
deepening the issues of operating system virtualization. Next, we proceed with
the illustration of Xen, the hypervisor considered in the tests, indicating the
interfacing mechanism with the various virtual machines: the hypercall.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
7
The third chapter talks about the fault injection tool starting from its architecture.
Next, we will explain the implementations of every modules which it is based on.
The last chapter, examine, more closely, the two tests that have been mentioned,
showing the steps needed to implement each. Finally, after each test, we will
carry out the final observations.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
8
Introduzione
Il presente lavoro si pone l’obiettivo di introdurre un nuovo approccio per valutare
l’affidabilità di sistemi in ambienti virtualizzati. Lo scenario di riferimento prevede
un hypervisor Xen su cui “poggeranno” diverse macchine virtuali, ciascuna
equipaggiata con un sistema operativo Unix like. In particolare, tale lavoro ha lo
scopo di produrre tre risultati: un tool per l’iniezione di guasti in ambienti
virtualizzati, una sperimentazione mirata a valutare il grado di affidabilità di un
singolo sistema installato nel contesto appena accennato, una seconda
sperimentazione volta a valutare il livello di isolamento ottenibile in ambienti
virtualizzati. I due test sono resi possibili dal tool introdotto all’inizio di questa
sezione.
Le motivazioni alla base di questo lavoro si basano su un’ampia forbice di
casistiche presenti in letteratura, dove, la presenza di bug nei sistemi software, si
è rivelata, in casi estremi, addirittura fatale per l’uomo. Ciò è dovuto al fatto che i
sistemi informatici hanno trovato spazio in qualsiasi contesto, critico e non.
Questa condizione stimola il mondo della ricerca a produrre soluzioni sempre più
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
9
efficaci che incrementino l’affidabilità dei sistemi. Come accennato in precedenza,
a differenza dei numerosi approcci in letteratura, la novità introdotta dal presente
lavoro consiste nell’applicazione di guasti nello strato software che garantisce la
condivisione delle risorse (CPU, memoria, …) tra macchine virtuali, e non nella
singola macchina virtuale. Non si vuole, quindi, conoscere il comportamento solo
della singola macchina testata ma si vuole estendere l’analisi anche alle altre
macchine virtuali senza trascurare l’entità che le tiene assieme: l’hypervisor. La
seconda sperimentazione delle due proposte, ha appunto questa finalità alla
quale si aggiunge l’intento di eseguire anche un’analisi prestazionale.
L’approccio utilizzato per la conduzione di entrambi i test si basa su un metodo
esistente in letteratura: Ballista. Tale meccanismo permette di valutare la
robustezza di un sistema senza conoscerne i dettagli implementativi. Infatti, il
modulo considerato nei test è rappresentato da una generica funzione (nel nostro
caso si parlerà di hypercall), a cui sono applicati valori in ingresso validi e non,
allo scopo di valutare le reazioni del modulo in corrispondenza di input non
necessariamente leciti.
Il presente lavoro si compone di quattro capitoli.
Nel primo capitolo si riporta una trattazione circa lo stato dell’arte riguardo la
teoria dell’affidabilità dei sistemi enfatizzando alcuni approcci possibili, presenti in
letteratura. In particolare, si dedicherà ampio spazio al metodo su cui si basa il
tool: Ballista. Infine, è riportata una breve descrizione dello scenario di riferimento
dei test, in modo che il lettore abbia, sin da subito, una visione d’insieme di ciò
che sarà, dettagliatamente, esposto nei capitoli successivi.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
10
Il capitolo secondo prevede un’ampia trattazione riguardo la teoria della
virtualizzazione, approfondendo uno degli argomenti attualmente più considerati:
la virtualizzazione dei sistemi operativi. A seguire, si procede con l’illustrazione di
Xen, l’hypervisor considerato nei test, mostrando i meccanismi d’interfacciamento
con le varie macchine virtuali: le hypercall.
Nel terzo capitolo ci si occupa di descrivere le caratteristiche del tool utilizzato nei
test, fornendo i dettagli implementativi di ciascun modulo che ne fa parte.
L’ultimo capitolo, esaminerà, più da vicino, i due test di cui si è accennato,
mostrando i passi necessari alla realizzazione di ciascuno. Infine, a valle di
ciascun test, sulla base dei risultati ottenuti, si faranno delle considerazioni
conclusive.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
11
Capitolo 1
Fault Injection e Robustness Testing
Attualmente l'uomo è circondato da sistemi che, per erogare un certo servizio,
ricorrono all'utilizzo di computer. Gli scenari che si possono ritrovare nella realtà
sono i più disparati e vanno dal campo della medicina a quello dell’intrattenimento,
dal settore dei trasporti (aerei, treni) a quello economico-finanziario. A questo punto,
una domanda sorge spontanea: ci si può realmente fidare di questi sistemi?
Purtroppo la risposta non è del tutto affermativa; basti pensare ai numerosi casi
riportati in letteratura in cui il comportamento anomalo di un sistema informatico ha
portato a conseguenze anche letali per l’uomo. Questo motiva il presente lavoro il
cui obiettivo è fornire un tool per il testing di sistemi operativi in ambiente
virtualizzato.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
12
1.1 Affidabilità dei sistemi software
Come accennato, i sistemi informatici trovano impiego in svariati scenari critici sia
sotto l'aspetto economico (business critical), sia in termini di affidabilità e sicurezza
(mission critical). Ad esempio, sistemi e-commerce, sistemi di pagamento
elettronico, fanno parte della categoria business critical, mentre, sistemi di controllo
del traffico ferroviario o sistemi utilizzati in ambito medico rientrano nella categoria
mission critical.
Come si può intuire dagli esempi, nei sistemi business critical, i danni, per quanto
gravi, si limitano a essere di carattere economico, mentre, nei sistemi mission
critical gli eventuali danni possono estendersi all'incolumità di persone e cose.
Prima di procedere all’illustrazione del concetto di dependability è riportata di
seguito una definizione generale del concetto di sistema che più volte ricorre nel
presente lavoro:
qualsiasi entità in grado di interagire con altre entità
(fisiche e non) è ascrivibile ad un sistema.
Si definisce servizio, invece, il comportamento del sistema percepito dall’utente. Un
servizio è corretto se è conforme alle specifiche. Qualora il servizio fornito non sia
corretto, si parla di failure.
Il concetto di affidabilità dei sistemi informatici fu introdotto per la prima volta nel
1930 nel contesto del calcolatore di Babbage [1] [2]. In seguito, con la prima
generazione di computer elettronici furono sperimentate le prime tecniche volte al
miglioramento dell'affidabilità, quali: i codici di controllo degli errori o ancora la
diagnostica per individuare componenti guasti [3-5]. Allo stesso tempo, J. Von
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
13
Neumann [6], EF Moore e CE Shannon [7] ed i loro successori svilupparono teorie
che prevedono l'introduzione della ridondanza per costruire strutture logiche
affidabili a partire da componenti meno affidabili, i cui difetti sono mascherati dalla
presenza elementi ridondanti.
Da questi esempi si può intuire che l'affidabilità è un argomento molto sentito sin
dagli albori dell'informatica proprio perché un malfunzionamento di qualsiasi entità o
natura può compromettere un servizio più o meno critico.
È di seguito proposta una definizione del concetto di dependability [8]: l'affidabilità
di un sistema informatico è la capacità di fornire un servizio conforme alle
aspettative.
Dello stesso autore si riporta un'altra definizione: l'affidabilità di un sistema consiste
nella capacità di evitare i disservizi più frequenti, la cui gravità oltrepassa il limite di
accettabilità.
La dependability è la proprietà di un sistema di elaborazione che incapsula diversi
attributi, quali: affidabilità, sicurezza, manutenibilità, ecc. Si potrebbe definirla
informalmente come l’abilità di un elaboratore di fornire un servizio di cui ci si può
fidare in maniera giustificata. Il servizio fornito da un sistema coincide con il suo
comportamento così com’è percepito dall’utente (non necessariamente umano)
attraverso un’interfaccia, mentre la funzione di un sistema rappresenta ciò che esso
è deputato a fare, secondo le relative specifiche funzionali. Perciò, il servizio messo
a disposizione sarà corretto quando esso è conforme alle specifiche, altrimenti
vorrà dire che si è verificato un malfunzionamento. In situazioni diverse, i vari
attributi della dependability assumeranno importanze differenti, ma l’efficacia
dell’approccio risiede proprio nell’esaustività e nell’integrazione dei diversi concetti
legati in qualche modo al problema dell’affidabilità. La dependability è una proprietà
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
14
di un sistema di elaborazione che, se affiancata a tutte le altre (funzionalità,
usabilità, prestazioni, costo), lo caratterizza completamente.
1.2 Minacce e approcci all’affidabilità
L'affidabilità di un sistema è minata da numerose cause che possono portare al
fallimento del sistema stesso: tra queste si fa riferimento a guasti hardware, errori di
progettazione hardware e/o software o ancora errati interventi di manutenzione. Per
guasto, s'intende uno stato improprio dell’hardware e/o del software del sistema
derivante dal fault di una sua parte, provocato ad esempio da fenomeni
d’interferenza o da errori di progettazione. Gli errori sono invece una parte dello
stato di un sistema che possono indurre lo stesso al fallimento; in questo caso il
sistema fornisce un servizio non conforme alle specifiche. L'origine di un errore è un
fault: è possibile, tra l'altro, che un fault sia la causa di molteplici errori. Infine, per
fallimento s'intende l’evento in corrispondenza del quale il sistema cessa di fornire
un servizio corretto. Nei sistemi attuali i guasti software sono la principale fonte di
errore. Un errore software equivale a un errore nel codice e, benché sia
permanente, si può manifestare in modo non deterministico. Per esempio, la sua
attivazione può avvenire durante l'esecuzione del programma a seguito di una
sequenza d’input non prevista a priori. Per questo motivo gli errori software sono i
più difficili da individuare.
Tra gli approcci più comuni finalizzati all’incremento dell’affidabilità di un sistema, si
annoverano il meccanismo di fault tolerance e la tecnica del robustness testing.
Uno degli aspetti che contribuisce significativamente all’affidabilità di un sistema è il
meccanismo di tolleranza ai guasti previsto dal sistema stesso. Formalmente, si
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
15
tratta della capacità che ha un sistema di erogare un servizio, eventualmente a un
livello di qualità ridotto, in presenza di uno o più componenti guasti. Chiaramente,
la fault tolerance di un sistema non garantisce l’immunità da qualsiasi tipo di guasto,
ma solo da quelli per cui è stata garantita, in fase di progetto, una protezione
adeguata. Alla base del meccanismo di fault tolerance c’è il test di robustezza che
misura quant’è ragionevole il comportamento di un sistema al verificarsi di una
situazione imprevista non contemplata dalle specifiche. Queste situazioni impreviste
possono essere di varia natura (dati di input non previsti, fallimenti di componenti
software o hardware esterni, etc…). Quello che il test fornisce in output è una serie
di indicazioni da seguire in fase di sviluppo mirate all’incremento del grado di fault
tolerance esibito da sistema.
1.3 Fault injection
Per fault injection s’intende una tecnica che prevede l’inserimento intenzionale di
guasti all’interno di un sistema allo scopo di studiarne, in seguito, il suo
comportamento. Tale tecnica è impiegata per testare i meccanismi di tolleranza ai
guasti di sistemi o di singoli componenti. In particolare, quest’approccio si pone
l’obiettivo di dare risposta ai seguenti interrogativi:
1. come reagisce un sistema al presentarsi di un guasto? È in grado di rilevare
ogni tipo di fault?
2. Il sistema riesce ad isolare un guasto? Ad esempio, nell’ipotesi che il guasto
venga iniettato in uno specifico componente, si vuole conoscere lo stato
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
16
dell’intero sistema;
3. Qual è lo stato del sistema a seguito di una procedura di recovery resa
necessaria dall’applicazione del guasto?
Figura 1.1 - Fault injection system
In figura 1.1 si riporta un possibile scenario applicativo della tecnica di iniezione
guasti, in cui si riconosce:
1. il sistema target che si vuole testare;
2. il modulo per l’iniezione dei guasti;
3. il workload generator che consente di simulare condizioni reali di funzionamento
sulla macchia target;
4. il modulo controllore che si occupa di monitorare lo stato degli altri moduli del
sistema;
5. il modulo monitor che supervisiona lo stato della macchina testata;
6. il modulo data collector che si occupa di raccogliere i log generati dal sistema .
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
17
1.4 Tipi di fault injection
È possibile operare una prima distinzione tra:
1. tecniche di “hardware fault injection”, rientrano tra i primi tentativi di introdurre
guasti direttamente nei dispositivi hardware; tale approccio è motivato dal fatto
che apparati quali CPU, bus, memoria sono tra le principali fonti di guasti;
2. tecniche di fault injection orientate alla valutazione della affidabilità del software:
come ampiamente discusso nei precedenti paragrafi i sistemi software sono
affetti da bug di cui spesso non si conoscono né la posizione all’interno del
codice, né le situazioni in cui possono rivelarsi né le conseguenze. Lo scopo di
quest’approccio è proprio quello di evidenziare i bug software e quindi fornire
spunti per una loro correzione.
Dopo un primo distinguo è possibile fornire altri dettagli per ciascuna delle due
categorie individuate sopra. Per quanto riguarda la categoria di “hardware fault
injection” è possibile operare una distinzione in tre classi:
1. iniezione di guasti mediante modifiche all’hardware o attraverso l’esposizione
dello stesso a fenomeni che possono alternarne il funzionamento (es.
interferenze elettromagnetiche);
2. simulazione di guasti ad “alto livello” servendosi di appositi modelli simulati al
computer;
3. emulazione di guasti harware via software (software implemented fault injection
- SWIFI).
Nell’ambito dell’iniezione di guasti software si riconoscono due possibili approcci:
1. Generic Software Fault Injection Technique (G-SWFIT) [9] ha come obiettivo
l’introduzione di errori software nel codice binario.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
18
2. Ballista, una metodologia per il Robustness testing che consiste
nell’applicazione di combinazioni di valori validi e non come parametri di
funzioni, allo scopo di valutare la robustezza di un sistema.
1.4.1 Mutazione del codice
La presente categoria contempla l’approccio G-SWFIT. Questa tecnica nasce come
soluzione ai casi in cui non è disponibile il codice sorgente del componente da
testare (adatta per i sistemi COTS). Si tratta di una tecnica di iniezione di guasti
basata su un set di fault injection operators che riproduce direttamente nel codice
eseguibile target la sequenza di istruzioni che emula il guasto software. Questi fault
injection operators sono la sintesi di uno studio che ha analizzato più di 500 guasti
software reali in diversi programmi.
Figura 1.2 - G-SWFIT
Si riporta, in figura 1.3, i tipi di guasto più frequenti individuati in [9]. La posizione
in cui l’iniezione deve essere eseguita è scelta attraverso l’analisi del codice
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
19
target resa possibile dal G-SWFIT tool, che consente di identificare la posizione
in cui un certo tipo di guasto può, realisticamente, verificarsi. Ad esempio, il tipo
di guasto “MIFS” riportato in figura 1.3 può essere solo iniettato laddove
presente un costrutto IF. L’analisi del codice target è basata sulla conoscenza di
come i costrutti di alto livello sono tradotti nei corrispondenti costrutti di basso
livello [10].
Figura 1.3 - Fault injection operators
Sulla base di quanto riportato in figura 1.3 è di seguito fornita una breve descrizione
di ciascuno tipo di fault individuati in [9] che hanno dato vita ai seguenti fault
injeciton operator:
1. OMIFS (Missing IF construct and surrounded Statements) indica l’assenza
del costrutto IF e dello statement ad esso associato;
2. OMFC (Missing Function Call) rappresenta l’omissione di una chiamata a
funzione;
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
20
3. OMLAC (Missing AND Expr in expression used ad branch condition) consiste
nell’omissione di una parte di un’espressione logica. Detta espressione deve
essere composta da almeno due sotto-espressioni collegate tra loro
mediante l’operatore AND;
4. OMIA (Missing IF constructs around statements) consiste nell’omissione di
una condizione IF che racchiude un certo blocco di istruzioni;
5. OMVAE (Missing Variable Assignment using an Expression) individua
assegnazioni a variabili tramite risultato di espressioni e ne simula
l’omissione;
6. OMVAV (Missing Variable Assignment using a Value) consiste nell’omissione
di assegnazioni a variabili con valori constanti;
7. OMVIV (Missing Variable Initialization using a Value) prevede l’omissione
dell’inizializzazione di variabili locali con valori costanti;
8. OMIEB (Missing IF construct plus statements plus ELSE before Statement)
comporta la rimozione del costrutto IF, degli statement e del costrutto ELSE
associati;
9. OMLOC (Missing “OR sub-expression” in Logical expression used in branch
Condition) prevede l’omissione di una parte dell’espressione logica impiegata
in un salto condizionato. Detta espressione deve essere composta da
almeno due sotto-espressioni collegate tra loro mediante l’operatore OR;
10. OMLPA (Missing Localized Part of the Algorithm) ha il compito di simulare
l’omissione di una parte, piccola e localizzata, dell’algoritmo;
11 OWVAV (Wrong Value Assigned to a Variable) prevede un’assegnazione ad
una varibile con un valore non ammissibile;
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
21
12. OWPFV (Wrong Variable in parameter of Function call); la sua funzione è
quella di simulare l’utilizzo di una variabile errata come parametro passato ad
una funzione;
13 OWAEP (Wrong Arithmetic Expression in Parameter of a functioncall)
consente di riprodurre lo scenario in cui viene passata ad una funzione una
espressione aritmetica invalida.
L’approccio Source Code Fault Injection (SCFI) si pone ad un livello di astrazione
più alto rispetto a G-SWFIT, prevedendo l’introduzione dei guasti direttamente nel
codice sorgente. Questa tecnica si concretizza nel tool [11] descritto in figura 1.4.
Figura 1.4 – Tool SCFI
Partendo dal codice sorgente del componente che s’intende testare, SCFI produce
in una prima battuta, una rappresentazione di tale codice sottoforma di un Abstract
Syntax Tree utile all’individuazione delle locazioni in cui iniettare il fault. Una volta
individuata una potenziale locazione in cui applicare il guasto, servendosi dei fault
operators discussi nel precedente paragarafo, il tool modifica la parte di codice
interessata in linea con l’operatore scelto, producendo in output la patch da
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
22
applicare al codice sorgente che sarà, infine, ricompilato. L’indisponibilità del codice
sorgente del componente da testare comporta la non applicabilità del metodo SCFI.
1.4.2 Robustness Testing
Come accennato nel paragrafo 1.2 la tecnica del robustness testing si pone
l’obbiettivo di testare il comportamento di un sistema a fronte di situazioni più o
meno usuali. Uno dei possibili approcci consiste nell’applicare in input al sistema
sequenze di valori ammissibili e non, valutandone, successivamente, il
comportamento.
Ballista [11] è una tecnica di rubustness testing fondata sull’approccio appena
illustrato. Si tratta di una metodologia basata, appunto, sull’applicazione di
combinazioni di valori validi e non come parametri delle system call o più in
generale delle funzioni. È una tipologia di testing “black box” dato che non è
richiesta la conoscenza dei dettagli implementativi del modulo che s’intende testare.
Lo scopo di quest’approccio è conoscere il comportamento di un modulo sottoposto
a determinati test rappresentati da combinazioni d’input validi e non. Un generico
caso di test prevede un modulo e una lista di valori di input scelti in maniera
opportuna. Una possibile strategia di scelta dei valori di input è esemplificata nella
figura 1.5.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
23
Figura 1.5 - Generazione dei casi di test per la funzione write()
Nell’esempio è mostrato un possibile metodo di scelta degli input. Il modulo in
questione è la funzione write (int filedes, const void *buffer,
size_t nbytes) che prende tre parametri, nell’ordine: il descrittore del file, un
buffer di memoria e il numero di byte che si vuole scrivere. Dato che
write()prende tre parametri di tre tipi diversi, Ballista sceglie, nel generico test,
uno dei possibili valori previsti per ciascun argomento.
Le linee guida proposte da Ballista, per la scelta dei valori da fornire in input ad un
modulo proseguono con i seguenti suggerimenti:
- Impostare un qualsiasi parametro con:
o un valore negativo;
o il minimo e il massimo valore rappresentabile;
- modificare il valore di un puntatore facendo in modo che, dopo l’iniezione, punti
ad un’area di memoria inesistente o ad una zona di memoria al limite di quella
ammissibile;
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
24
- nel caso di accesso a file, prevedere l’apertura contemplando tutte le possibili
combinazioni di permessi;
Oltre a fornire numerosi spunti su come strutturare una campagna di fault injection,
Ballista offre diversi suggerimenti per la fase di raccolta dei risultati. A valle di
ciascun test, è prevista la seguente classificazione dei risultati:
1. Catastrophic. I fallimenti di questo tipo si verificano quando il sistema testato si
corrompe o la macchina va in crash riavviandosi successivamente;
2. Restart. Questo tipo di fallimento si manifesta quando il modulo testato non
riesce a restituire il controllo al chiamante. Tale stato è definito in gergo “hung” e
la sua risoluzione è affidata nella maggior parte dei casi all’intervento manuale
dell’utente che si occuperà del riavvio del modulo in questione;
3. Abort. Un abort si verifica quando il processo corrispondente al modulo testato
termina in modo anomalo;
4. Silent. Si tratta di un tipo di fallimento che il sistema non è in grado di rilevare;
5. Hindering. Si tratta di situazioni in cui il modulo richiamato restituisce un codice
di errore di cui non è nota la procedura di recovery prevista.
1.5 Scopo della tesi
Lo scopo di questo lavoro è fornire strumenti per misurare l’affidabilità di
un’infrastruttura basata su virtualizzazione, in cui più macchine virtuali coesistono.
In questo contesto, è fondamentale garantire la disponibilità delle macchine virtuali,
e impedire che i malfunzionamenti di una macchina virtuale compromettano l’intera
infrastruttura. L’approccio proposto in questa tesi permette di valutare la robustezza
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
25
dell’infrastruttura e il grado d’isolamento tra macchine virtuali in presenza di una
macchina virtuale guasta. L’infrastruttura considerata è costituita dall’hypervisor
Xen, i cui dettagli saranno esposti nel capitolo successivo.
Figura 1.6 – Scenario di riferimento per la conduzione dei test
Il tool di fault injection presentato in questo lavoro prevede l’emulazione degli effetti
dei guasti hardware e/o software, applicati a un contesto virtualizzato, mediante
modifiche ai codici sorgente delle funzioni che si vuole testare.
Sulla base del modello introdotto da Ballista, il tool prevede l’alterazione dei valori
assunti dai parametri delle funzioni secondo le specifiche dettate dall’utente. A tal
proposito le modifiche del codice di cui si è parlato in precedenza sono necessarie
per poter stabilire a run-time il tipo di alterazione da applicare e il parametro
interessato.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
26
Nella fattispecie data una generica funzione function(type_1 arg_1, ...,
type_n arg_n), ad ogni sua invocazione, il tool effettua la corruzione di uno dei
parametri secondo uno dei possibili tipi di guasto previsti (es., corruzioni random di
bit). Con riferimento alla figura 1.6, le funzioni target del tool fanno parte dello strato
software, che in ambiente virtualizzato, permettono di interfacciare il sistema
operativo con l’hardware sottostante. Tali funzioni sono denominate hypercall.
Il tool, inoltre, permette di individuare il grado di isolamento messo in atto
dall’hypervisor: con riferimento alla figura 1.6, si vuole verificare se la corruzione di
uno di questi sistemi (A) incide sul funzionamento e sulle prestazioni degli altri (B).
1.6 Struttura della tesi
Nei capitoli successivi si discuteranno più approfonditamente dei fondamenti teorici
della virtualizzazione (capitolo 2) ponendo particolare enfasi all’hypervisor che sarà
utilizzato nei test (capitolo 3). Successivamente, si propone una descrizione
dettagliata del funzionamento del tool e della sua implementazione (capitolo 4).
Infine, è proposto un caso concreto dell’utilizzo del tool con relativa discussione dei
risultati ottenuti (capitolo 5).
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
27
Capitolo 2
Infrastrutture di virtualizzazione
L'informatica odierna è sempre più attenta a nuove forme di distribuzione dei
carichi di lavoro, sia per massimizzare l'utilizzo di ogni singola risorsa, sia per
economizzare la gestione e i costi dei mezzi a disposizione.
Per questo motivo oggi si ricorre sempre più spesso a termini tecnici quali cloud
computing e virtualizzazione, spesso senza essere effettivamente consci del loro
significato profondo, delle implicazioni e dei benefici tecnologici che queste
metodologie apportano. Concentrandosi sul solo aspetto della virtualizzazione, è
riduttivo accostare tale termine unicamente alla sfera dei server e degli altri
processi che avvengono in queste macchine. Infatti, "virtualizzare", cioè applicare
il principio per cui si genera una versione simulata di una risorsa effettivamente
esistente dal punto di vista fisico, non è da riferirsi soltanto al mondo dei server.
Quest'ultima è una declinazione specifica del principio e non una sua definizione.
In generale vale che qualsiasi sia la risorsa a disposizione, questa possa essere
teoricamente virtualizzata, sia essa software o hardware. Partendo da questo
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
28
presupposto, diverrà ancora più semplice comprendere le differenti forme di
virtualizzazione oggi esistenti, che coinvolgono diversi tipi di risorse.
2.1 Che cos’è la virtualizzazione?
La virtualizzazione è concettualmente simile all’emulazione. Con l’emulazione, un
sistema “finge” di essere un altro sistema. Con la virtualizzazione, un sistema “finge”
di essere due o più repliche dello stesso sistema. Molti degli attuali sistemi operativi
prevedono meccanismi di virtualizzazione delle risorse. Ciascun processo in
esecuzione immagina di essere l’unico e quindi di avere l’intera macchina a
disposizione. La CPU e la memoria sono virtualizzate. Se un processo tenta di
“impossessarsi” indefinitamente della CPU, il sistema operativo ne effettua la
prelazione in favore di un altro processo in modo da garantire una equa condivisione
delle risorse tra i processi. Similmente, un processo in esecuzione, possiede un
proprio spazio d’indirizzamento virtuale mappato in memoria fisica dal sistema
operativo: il processo ha l’illusione di avere l’intera memoria a disposizione. Anche i
dispositivi hardware sono virtualizzati: un processo può usare le Berkeley Socket API
per accedere a interfacce di rete senza preoccuparsi delle altre applicazioni.
2.1.2 Perché virtualizzare?
Il motivo principale è lo stesso che ha dato luogo al multitasking nel caso dei sistemi
operativi: i computer hanno una potenza di calcolo ben superiore a quella necessaria
per eseguire un singolo task. Un numero sempre crescente di organizzazioni si ritrova
attualmente a dover gestire una certa quantità di server ciascuno dei quali “ospita” un
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
29
singolo task: la virtualizzazione offre la possibilità di eseguire su una singola
macchina fisica un certo numero di server virtuali garantendo una migliore
utilizzazione delle risorse fisiche e mantenendo allo stesso tempo un livello di
sicurezza elevato grazie a politiche che permettono di isolare tra loro macchine
ospitate sullo stesso server.
2.1.3 Macchina virtuale
Per prima cosa, è proposta in seguito una definizione formale di Virtual Machine
Monitor (o Hypervisor). Un Virtual Machine Monitor (VMM) è uno strato software che
si pone tra uno o più sistemi operativi e l'hardware, dando l'illusione che ciascun
sistema operativo in esecuzione detiene il controllo dell'hardware sottostante. Nella
fattispecie, è lo stesso Hypervisor ad avere il controllo della macchina fisica,
garantendo una condivisione della stessa tra i vari sistemi operativi ospiti. Si
definisce macchina virtuale un contenitore software, totalmente isolato, che può
eseguire i propri sistemi operativi e applicazioni come fosse un computer fisico. Una
macchina virtuale si comporta esattamente come un computer fisico ed è dotata di
propri CPU, RAM, disco rigido e NIC (Network Interface Card) virtuali basati, ad
esempio, su software. Un sistema operativo non è in grado di distinguere una
macchina virtuale da una macchina fisica, né possono farlo le applicazioni o altri
computer in rete. La stessa macchina virtuale si comporta come se fosse un vero e
proprio computer, nonostante sia costituita interamente di software e non contenga in
alcun modo componenti hardware.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
30
2.1.4 Cenni storici
L'introduzione del concetto di virtualizzazione risale agli anni '60, dove fu introdotto
come paradigma per il partizionamento delle costose risorse hardware tra utilizzatori
diversi. Un caso concreto è rappresentato dal sistema IBM CP/CSM. Si tratta di un
meccanismo ripartito su due livelli: un componente denominato control program
esegue direttamente sull'hardware svolgendo il ruolo di VMM (virtual machine
manager), mettendo a disposizione diverse interfacce al livello superiore denominato
conversational monitor system, ovvero un sistema operativo replicato su ciascuna
macchina virtuale. L'evoluzione di tale meccanismo si concretizza nel VM/370 che,
alle caratteristiche del suo predecessore, aggiunge la possibilità di eseguire sistemi
operativi diversi sulla medesima macchina virtuale. Gli anni '70 sono caratterizzati
dalla massiccia diffusione dei sistemi operativi multitasking. Negli anni '80/'90,
complice un forte abbattimento dei costi dell'hardware, si assiste a un parziale
abbandono delle tecniche di virtualizzazione soprattutto per quanto riguarda
l'infrastruttura server. Sono gli anni in cui vige il paradigma “one application, one
server” che comporta, di contro, un incremento massiccio di server fisici da
configurare e un sottoutilizzo delle risorse hardware tant'è che alla fine degli anni '90
si sente, sempre più forte, la necessità di razionalizzare le risorse facendo tornare di
impeto le tecniche di virtualizzazione lato server.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
31
2.1.5 Alcuni esempi di virtualizzazione
Facendo riferimento ad un generico sistema computer la virtualizzazione può essere
applicata a tre livelli:
1. Linguaggi ad alto livello;
2. Sistema Operativo;
3. Hardware.
2.1.5.1 Virtualizzazione applicata ai linguaggi di alto livello
Il linguaggio Java rappresenta uno degli esempi di maggiore successo per quel che
riguarda la virtualizzazione applicata ai linguaggi di alto livello. La macchina virtuale
java si occupa di eseguire programmi tradotti in bytecode dopo una prima
compilazione. La chiave della portabilità di Java consiste nella disponibilità di
implementazioni della macchina virtuale per diversi ambienti operativi come recita lo
slogan write once, run everywhere (scrivi una volta, esegui dappertutto). La macchina
virtuale realizza, infatti, un ambiente di esecuzione omogeneo, che nasconde al
software Java (e quindi al programmatore) qualsiasi specificità del sistema operativo
sottostante.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
32
Figura 2.1 – Un esempio di virtualizzazione a livello applicativo: Java
2.1.5.2 La virtualizzazione dei sistemi operativi
Come riportato nell’introduzione, lo scenario su cui sono stati condotti i test prevede
un certo numero di macchine virtuali (ciascuna delle quali ospita a sua volta un
sistema operativo), in esecuzione su una certa macchina fisica. A fare da collante tra
macchine virtuali e macchina fisica ritroviamo uno strato software denominato
hypervisor, che si occupa, tra le altre cose, di gestire la condivisione delle risorse
fisiche tra i vari sistemi operativi. Attualmente, una delle aree nella quale molte
aziende dell’information technology stanno concentrando il loro impegno è proprio la
virtualizzazione dei sistemi operativi. Gli ambiti applicativi delle macchine virtuali sono
molteplici ed eterogenei fra loro, poiché la virtualizzazione sta diventando sinonimo di
sicurezza informatica ed affidabilità del sistema. L’hypervisor può, infatti, controllare
ed interrompere eventuali attività pericolose, e ciò fa sì che si usino macchine virtuali
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
33
sempre più frequentemente in ambito di ricerca e collaudo di software. Uno
sviluppatore di software potrà quindi eseguire la sua applicazione in diversi ambienti
senza dover disporre di più macchine fisiche. Un amministratore di sistema potrà
testare uno scenario complesso che vede interagire più servizi su host diversi,
ricreando tale scenario su più macchine virtuali (VM) ospitate in una singola macchina
fisica.
È di seguito riportato un elenco dei vantaggi derivanti dall’utilizzo di soluzioni basate
sulla virtualizzazione.
1. Aumento dell’affidabilità del sistema: sarà, infatti, possibile dedicare una
macchina virtuale all’esecuzione di pochi servizi che notoriamente non vanno
in conflitto tra di loro. Inoltre l’hypervisor isolerà le macchine guest in
esecuzione sullo stesso host affinché eventuali problemi che compromettono il
funzionamento di una singola macchina virtuale, non influenzino la stabilità
delle altre;
2. Consolidamento dei server: Molte aziende hanno visto crescere
vistosamente il numero dei server proprio a causa dell’aumento dei servizi da
fornire ai propri utenti. Attraverso la virtualizzazione si possono eseguire più
macchine virtuali nella stessa macchina fisica riducendo drasticamente il
numero di server;
3. Riduzione del Total Cost of Ownership (TCO): il consolidamento ad un
numero inferiore di server permette una notevole riduzione dei costi legati
all’energia utilizzata per alimentare i server stessi e per mantenere la
temperatura ambientale adatta alle sale server. Inoltre si riducono i costi di
acquisto e i canoni di manutenzione dei server fisici;
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
34
4. Disaster Recovery: l’intero sistema operativo “guest” può essere facilmente
salvato e ripristinato riducendo notevolmente i tempi d’indisponibilità in caso di
guasto;
5. Alta disponibilità: se è presente un’infrastruttura di server fisici con
caratteristiche hardware tra loro compatibili e questi server condividono una
area dati (storage) sulla quale risiedono le macchine virtuali, sarà possibile
spostare l’esecuzione di una macchina virtuale su un altro host in caso di
failure. Alcuni sistemi prevedono lo spostamento automatico delle macchine
virtuali tra i vari host in funzione al carico;
6. Esecuzione di applicazioni legacy: è frequente che alcune organizzazioni
utilizzino applicazioni sviluppate per sistemi operativi che girano su hardware
ormai obsoleto, non supportato o addirittura introvabile. Attraverso la
virtualizzazione si possono continuare ad utilizzare quelle applicazioni che
diversamente dovrebbero essere migrate ad una architettura più attuale
affrontando i costi relativi al porting e al debug.
2.1.6 Tipi di virtualizzazione
In letteratura sono proposti diversi approcci per la virtualizzazione. Tutte tecniche di
virtualizzazione ad oggi impiegate danno l’illusione di utilizzare un sistema operativo
stand alone, non virtualizzato. Tali tecniche sono:
1. Emulazione: Con l’emulazione l’hypervisor ha il ruolo di simulare l’intero
hardware set in modo da permettere al sistema operativo di essere eseguito
senza modifiche. Il software di virtualizzazione ha il compito di esibire al
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
35
sistema operativo guest un’architettura hardware completa a lui nota,
indipendentemente dall’architettura hardware presente sulla macchina host.
Figura 2.2 - Emulazione
I limiti in termini di prestazioni e di funzionalità di quest’approccio sono
facilmente intuibili: gli emulatori, infatti, offrono al sistema operativo guest
un’architettura hardware standard impedendo quelle che potrebbero essere le
funzionalità alle quali si è abituati, ad esempio quelle implementate in
hardware. Inoltre, l’emulatore deve provvedere all’interfacciamento della CPU,
della memoria e dell’I/O tra sistema host e sistema guest.
Per quanto riguarda la memoria, ciascuna macchina virtuale riceve una
sequenza di indirizzi che utilizza in modo esclusivo; il ruolo dell’emulatore
consiste nel sommare all’indirizzo della cella richiesta una costante che
rappresenta l’entry dell’area di memoria assegnata alla macchina virtuale.
Un modo di gestire la CPU invece, consiste nell’interpretare il flusso di
istruzioni provenienti dal sistema guest ed eseguire sul processore host una
serie di istruzioni semanticamente equivalenti. Questa strategia ha come effetto
collaterale un notevole calo di prestazioni ed è pertanto inefficace soprattutto
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
36
quando bisogna emulare sistemi guest che richiedono processori di velocità
equivalente al processore dell’host.
2. Full Virtualization: la Full Virtualization detta anche Native Virtualization è
molto simile all’emulazione. Come nell’emulazione, i sistemi operativi guest
girano senza alcuna modifica in macchine virtuali ospitate sul sistema host. La
differenza rispetto all’emulazione risiede nel fatto che i sistemi operativi guest
devono essere compatibili con l’architettura hardware della macchina fisica. In
questo modo molte istruzioni possono essere eseguite direttamente
sull’hardware senza bisogno di un software di traduzione garantendo
prestazioni superiori rispetto all’emulazione. Esempi di software che utilizzano
la full virtualization sono VMware, Parallel Desktop, Virtual Box, Win4Lin Pro e
Xen, limitatamente ai sistemi operativi proprietari non modificabili.
Figura 2.3 – Full Virtualization
3. Paravirtualization: Un’altra tecnica di virtualizzazione è la paravirtualization.
L’hypervisor presenta alle macchine virtuali una versione modificata
dell’hardware sottostante, mantenendone tuttavia la medesima architettura. Il
sistema operativo in esecuzione sulle macchine virtuali è invece modificato per
evitare alcune particolari chiamate di sistema. Non viene invece modificata
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
37
l’Application Binary Interface e pertanto le applicazioni possono essere
eseguite senza modifiche. Questa tecnica permette di ottenere un decadimento
delle prestazioni minimo rispetto al sistema operativo non virtualizzato, dato
che le istruzioni provenienti dalle macchine virtuali sono eseguite quasi tutte
direttamente sul processore senza che intervengano modifiche. Esempi di
paravirtualizzazione sono Xen e User-mode Linux.
Figura 2.4 – Paravirtualization
4. Operating System level Virtualization: questo approccio non prevede
l’utilizzo di alcun Hypervisor ma la virtualizzazione è ottenuta mediante la
condivisione dell’immagine di un sistema operativo tra più guest. Tali sistemi
guest si possono considerare a tutti gli effetti istanze del sistema operativo host
con un proprio file system, configurazione di rete e applicazioni. La possibilità
di condividere la memoria tra i vari sistemi host costituisce il principale
vantaggio di questa tecnica. Inoltre, è previsto un significativo risparmio di
memoria dovuto al fatto che i vari sistemi guest non necessitano un kernel
privato, ma ne utilizzeranno uno in comune. Tuttavia, l’impossibilità di eseguire
su un unico host macchine con sistemi operativi diversi costituisce un limite
evidente dell’ Operating System Virtualization. Altro effetto collaterale è il ridotto
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
38
isolamento tra le macchine guest: una qualsiasi anomalia di un solo guest può
influire negativamente sugli aspetti sia comportamentali sia prestazionali delle
altre macchine.
Figura 2.5 – Operating System Level Virtualization
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
39
2.2 Xen hypervisor
Xen è un hypervisor nato da un progetto open source iniziato presso l’università di
Cambridge. É alla base di innumerevoli applicazioni commerciali e open source quali
server virtualization, Infrastructure as a Service (IaaS), desktop virtualization, security
applications, sistemi embedded. É, inoltre, alla base dei principali sistemi di cloud
computing.
2.2.1 Architettura
L'ambiente virtuale Xen è costituito da diverse componenti che cooperano tra loro.
Figura 2.6 – Architettura di Xen
Con riferimento alla figura 2.6 l’architettura di Xen prevede:
1. L'hypervisor Xen che, come più volte detto, “poggia” direttamente sull'hardware
(bare metal hypervisor) occupandosi dello scheduling della CPU e del
partizionamento della memoria tra le varie macchine virtuali in esecuzione. Il
suo compito principale è quello di garantire l'esecuzione di più macchine virtuali
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
40
su una certa infrastruttura hardware, offrendo, quindi la condivisione delle
risorse fisiche a disposizione. È da notare che l’hypervisor non si occupa delle
normali operazioni di I/O come l’accesso alla rete, l’accesso allo storage, attività
di monitoring, ecc;
2. Domain 0 (o Dom0). Si tratta di una macchina virtuale con kernel Linux
modificato. È l'unica macchina virtuale in esecuzione su Xen che gode di
speciali privilegi di accesso alle risorse di I/O ed ha, inoltre, la possibilità di
interagire con le altre macchine virtuali (Domain U o DomU) in esecuzione. Un
qualsiasi ambiente di virtualizzazione basato su Xen richiede la presenza di un
Domain 0 prima che qualsiasi altra macchina ospite possa essere avviata. Il
Domain 0, prevede, inoltre, la presenza di due driver a supporto delle richieste
di accesso al disco e alla rete, generate dai guest domain (il Network Backend
Driver e il Block Backend Driver). Il Network Backend Driver comunica
direttamente con l'interfaccia di rete locale (fisica, non virtuale) con lo scopo di
processare le richieste di accesso alla rete, provenienti dai DomU. Il Block
Backend Driver comunica con il disco locale e legge o scrive i dati sui volumi in
base alle richieste provenienti dai Domain U. Tra le altre cose, il Dom0 è
responsabile di compiti amministrativi quali la creazione di un DomU o la
gestione dei DomU correntemente in esecuzione.
3. Domain U. Questo tipo di dominio non avendo accesso diretto all'hardware
(contrariamente al Dom0), è spesso identificato col termine di dominio non
privilegiato (unprivileged domain). A tal proposito, occorre distinguere tra i
Domain U PV (paravirtualizzati) e i Domain U HVM (Hardware Virtual Machine).
I Domain U PV sono quelle macchine virtuali che utilizzano la tecnica della
paravirtualizzazione. Il loro kernel è stato modificato per ottimizzarne
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
41
l’esecuzione su Xen Hypervisor. Si tratta di macchine virtuali con sistemi
operativi Linux, Solaris, FreeBSD e altri sistemi open source. A livello
accademico è stata creata anche una versione paravirtualizzata di Windows XP
in collaborazione con Microsoft. I Domain U HVM sono invece macchine virtuali
completamente virtualizzate in quanto dotate di sistemi operativi con kernel non
modificabile, ad esempio tutti i sistemi operativi di Microsoft: quello appena
citato è senza dubbio uno dei maggiori benefici apportati dall’approccio HVM. I
domini paravirtualizzati sono “coscienti” che stanno operando su Xen Hypervisor
e che ci sono altri Domain U in esecuzione. Sono dotati quindi di due driver per
gestire le loro operazioni sulla rete e sullo storage che s’interfacciano con i
driver di back end presenti sul Dom0. Diversamente, i Domain U HVM Guest
non hanno questi driver. La comunicazione con le risorse di I/O avviene invece
attraverso un processo speciale chiamato Qemu-dm che viene istanziato sul
Dom0: ogni Domain U HVM Guest in esecuzione richiede il corrispondente
demone Qemu sul Domain 0. Questo demone si occupa di gestire tutte le
richieste di accesso al disco e alla rete da parte del domino HVM che, essendo
ignaro del fatto che esegue su Xen, accede ai driver dei dispositivi i quali
faranno riferimento non alle risorse fisiche bensì a quelle emulate da qemu. Il
dominio HVM è caratterizzato, inoltre, da uno strato software detto Xen Virtual
Firmware. Si tratta di un bios virtuale che assicura al sistema operativo guest di
ricevere, in avvio, tutte le istruzioni che si aspetta, mascherando così
l’architettura sottostante.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
42
Figura 2.7 – Accesso periferiche dei Dom U HVM
Per meglio chiarire il ruolo svolto dal Dom0 e dal DomU (paravirtualizzato) si faccia
riferimento allo scenario di figura 2.8 in cui un pacchetto deve essere inviato da
un'applicazione che esegue su un DomU.
Figura 2.8 – Meccanismo split device driver
Per prima cosa, detto pacchetto attraversa lo stack TCP/IP, come avverrebbe di
norma; alla fine della pila TCP/IP, ad “attendere” il pacchetto non troviamo il driver
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
43
di rete bensì una routine, denominata Split Device Driver, che si occupa di trasferire
i dati provenienti dall'applicazione in un'area di memoria condivisa con il Dom0.
L'altra metà dello Spit Device Driver, che si trova nel Dom0, legge i pacchetti dal
buffer condiviso trasferendoli al device driver dell'interfaccia di rete (fisica). Il
segmento di memoria è stato preventivamente condiviso (tra Dom0 e DomU)
usando un meccanismo denominato Xen grant table e “pubblicizzato” da XenStore
(entrambi gli argomenti sono approfonditi in seguito). Si noti che lo split network
device è lo stesso indipendentemente dall’interfaccia di rete; questo favorisce, tra le
altre cose, il porting di sistemi su Xen. In sintesi, un driver prevede tre componenti:
1. lo split driver è tipicamente più semplice possibile; il suo compito
principale è quello di trasferire dati dal DomU al Dom0 mediante la
condivisione della memoria;
2. il driver reale, che esiste indipendentemente da Xen e che, quindi, non
può essere considerato parte di esso;
3. un multiplexer che fa parte, tipicamente, del sistema operativo e si
occupa di instradare i dati verso la corretta routine di gestione.
2.2.2 Configurazioni
La configurazione più semplice di Xen prevede il solo Dom0. In questo scenario,
Xen si comporta come un hardware abstraction layer. Si tratta di una configurazione
non utile in genere. Tipicamente, un sistema Xen possiede almeno un DomU guest
in esecuzione (Figura 2.9).
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
44
Figura 2.9 – Una semplice configurazione di Xen
In questo esempio tutto l'hardware è controllato dall'host nel Domain 0; questo non
è sempre l'ideale, infatti, se un driver contiene un bug può andare in crash nel
Dom0 causando il crash anche degli altri domini. Pertanto, è spesso utile isolare un
driver in un dominio ad esso dedicato. Un driver domain è, quindi, un dominio Xen
non privilegiato che è responsabile della gestione di alcuni driver; viene eseguito in
un kernel minimale e oltre a disporre del driver del dispositivo mette a disposizione
degli altri domini il driver di back-end per il suddetto device. Quindi, se il driver
fallisce, gli altri domini (incluso il Dom0) sopravvivono e, quando il driver domain
viene riavviato, è possibile usarlo di nuovo. Tale approccio comporta una riduzione
del sovraccarico del Dom0 oltre ad un significativo miglioramento del livello di
sicurezza: i driver, infatti, sono la principale causa del fallimento di un sistema
operativo. Isolare il driver quindi, va a beneficiare sulla sicurezza dell'intero sistema.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
45
Figura 2.10 – Architettura per l’esecuzione di applicazioni legacy
Un'altra possibile configurazione segue dalla necessità di eseguire applicazioni
legacy in concorrenza con applicazioni recenti; in tale circostanza si ricorre a guest
non modificabili. Chiaramente, in questo tipo di configurazione, i guest non
modificati non usano direttamente gli split device driver. Usano, invece, device
driver emulati nel Dom0 come riportato in figura 2.10.
2.2.3 Xen e i meccanismi di protezione dell’architettura IA-32
Nel modello di protezione classico tipico delle architetture IA-32 sono previsti
quattro livelli di privilegi denominati ring. Il ring con priorità maggiore coincide con lo
zero; si tratta del livello in cui il kernel esegue (modo supervisore). Il livello più
basso corrisponde al ring tre; è qui che eseguono le applicazioni utente (modo
utente). Eseguire alcune istruzioni, denominate “privilegiate”, nel ring tre comporta
la generazione di una trap con conseguente passaggio dal modo utente al modo
supervisore. Il ring uno e due non sono stati usati negli anni (ad eccezione di OS/2).
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
46
L'introduzione di un hypervisor al di sotto del sistema operativo viola il principio per
cui il sistema operativo deve essere eseguito con il privilegio maggiore. Per
proteggere l’hypervisor da comportamenti inaspettati dei sistemi operativi guest e
per garantire isolamento tra le macchine virtuali, essi devono essere eseguiti con
privilegi inferiori. In architetture diverse da IA32 (ad esempio IA64) l'assenza dei
ring uno e due imponeva una modifica all'hypervisor Xen (che eseguiva nel ring
zero) con il con il conseguente “spostamento” del sistema operativo nel ring tre
(insieme alle applicazioni utente). Il tutto è mostrato in figura 2.11.
Figura 2.11 – Utilizzo dei ring nei sistemi con architettura IA64
La presenza di quattro ring (IA32) semplifica l'introduzione di Xen; infatti, basterà
“spostare” il sistema operativo nel ring 1 lasciando il ring 0 all'hypervisor (figura
2.12).
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
47
Figura 2.12 – Utilizzo dei ring nei sistemi con architettura IA32
2.2.4 Gestione della memoria
Per quel che concerne la virtualizzazione della memoria, i progettisti di Xen hanno
preso due decisioni:
1. I sistemi operativi guest sono responsabili dell'allocazione e della gestione
delle tabelle delle pagine hardware, con un minimo coinvolgimento di Xen che
garantisce sicurezza e isolamento;
2. Xen occupa una porzione ben definita dello spazio di indirizzamento in modo
da evitare il ricorso alla pulizia del Translation Lookaside Buffer (TLB) ogni
volta che l'hypervisor è interessato da un cambiamento di contesto.
In figura 2.13 sono proposti alcuni scenari che prevedono una diversa gesitone
della memoria a seconda dell’architettura disponibile:
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
48
1. In presenza di un’architettura a 32 bit, Xen occupa i primi 64MB dello spazio di
indirizzamento a 4GB;
2. Se l’architettura della macchina prevede Physical Address Extension (PAE),
Xen avrà a disposizione i primi 168MB.
Figura 2.13 – Gestione della memoria
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
49
2.2.5 Analogie con i sistemi operativi
La scrittura di codice per Xen è, per molti versi, simile alla scrittura di codice per
UNIX. La figura 2.14 mostra le analogie tra Xen e un sistema Unix like.
Unix Xen
System Calls Hypercalls
Signals Events
Filesystem XenStore
POSIX Shared Memory Grant Tables
Figura 2.14 – Analogie tra Xen e Unix
2.2.5.1 Hypercalls
Come accennato nei paragrafi precedenti, siccome il kernel esegue nel ring 1 (nel
ring 0 c'è Xen!), non gli è concesso di fare qualsiasi operazione. Qualcosa di simile
accade ai programmi che eseguono nello spazio utente (ring 3): effettuare una
stampa sullo schermo, inviare dati in rete o leggere un input dalla tastiera sono
operazioni non effettuabili in modalità non privilegiata. La soluzione, com'è noto, è
quella di usare le system call: si tratta di un meccanismo formale in cui viene
chiesto al kernel di eseguire un compito specifico. Le system call lavorano tutte allo
stesso modo, indipendentemente dal sistema operativo o piattaforma:
1. Effettuano il push dei parametri nei registri o nello stack;
2. Generano un’interrupt software (trap) specifico;
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
50
3. La generazione dell'interrupt comporta il passaggio dal modo di esecuzione
utente (non privilegiato) al modo kernel (privilegiato);
4. Con i privilegi ottenuti viene processata la system call;
5. si riporta il sistema al modo di funzionamento utente ritornando il controllo al
chiamante.
In generale, l'architettura x86 prevede l'utilizzo dell'interrupt 80h per le system call,
tuttavia, release recenti prevedono l'utilizzo di istruzioni speciali quali
SYSENTER/SYSEXIT o SYSCALL/SYSRET.
Nel caso di Xen la soluzione al problema presentato a inizio paragrafo si basa sullo
stesso meccanismo sopraesposto. Un sistema guest invoca una hypercall nello
stesso modo in cui un'applicazione chiama un system call con la differenza, nel
caso delle hypercall, che l'interrupt generato è 82h.
Il presente meccanismo di funzionamento è stato in uso fino alla versione 3 di Xen
(la versione più recente corrisponde alla 4.2); le successive versioni prevedono un
meccanismo basato sulla creazione di un'hypercall page. Si tratta di un'area di
memoria mappata nello spazio di indirizzamento di un guest e creata all'avvio del
sistema. Una specifica hypercall è eseguita invocandola presso l'indirizzo della
hypercall page in cui risiede.
La riga 5 di figura 2.15 mostra il meccanismo di invocazione di una hypercall. Nella
fattispecie, viene richiamato un indirizzo dato dal numero della hypercall che
s'intende richiamare moltiplicato per 32. Poiché la dimensione di una pagina
sull'architettura x86 è di quattro kilobyte, si possono avere al massimo 128
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
51
hypercall. Al momento della scrittura esistono 37 hypercall più altre 8 dipendenti
dalla specifica architettura.
1 #define _hypercall0(type, name) \
2 ({ \
3 __HYPERCALL_DECLS; \
4 __HYPERCALL_0ARG(); \
5 asm volatile (__HYPERCALL \
6 : __HYPERCALL_0PARAM \
7 : __HYPERCALL_ENTRY(name) \
8 : __HYPERCALL_CLOBBER0); \
9 (type)__res; \
10 })
11
Figura 2.15 – Esempio macro hypercall0
Per fare un paragone, la versione 7 del sistema operativo Unix supporta ben 64
system call, mentre in un moderno kernel FreeBSD sono implementate all'incirca
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
52
450 system call. Il fatto di aver un numero ristretto di hypercall risponde a pieno alla
filosofia di Xen, “less is more”, la cui traduzione molto informale, potrebbe essere:
l'hypervisor non dovrebbe fare nulla che non sia strettamente necessario al
funzionamento dei guest.
Per ragioni di efficienza, la filosofia di Xen prevede alcune hypercall che eseguono
in batch più operazioni (scheduling, aggiornamento della tabella delle pagine...) in
un solo colpo.
Le successive due linee di codice comunicano al compilatore GCC i registri del
processore da utilizzare. Il valore di ritorno (__res) è memorizzato nel registro EAX
e l'argomento in EBX; nel caso di hypercall con più di un argomento sono utilizzati
anche gli altri registri come ad esempio ECX.
Quando si scrive del codice in ambiente utente, non è usuale entrare
“manualmente” nell'ambiente kernel. Il passaggio dal modo di esecuzione user al
modo kernel è ottenuto tramite delle funzioni “wrapper”, denominate system call,
che fanno da tramite tra i due ambienti dei esecuzione. Allo stesso modo si
comportano le hypercall che consentono, con riferimento all'architettura x86, il
passaggio dal ring 1 (kernel) al ring 0 (hypervisor). Torneremo a parlare
dell'hypercall nei paragrafi successivi in maniera più approfondita.
2.2.5.2 Events
In un sistema Unix, è possibile utilizzare i segnali per garantire la comunicazione tra
due entità; si tratta di un meccanismo asincrono che permette ad una entità
qualsiasi, esterna ad un processo, di “avvertire” lo stesso processo circa
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
53
l'occorrenza di un dato evento. L'entità esterna può essere il kernel o un altro
processo. Il meccanismo analogo in Xen è noto come “event mechanism”. Una
delle prime cose che un kernel guest deve fare all'avvio è di registrare una callback
che sarà usata per la notifica di eventi. Quando un guest riceve la notifica di un
evento, vengono settati dei flag per indicare, appunto, che detto evento si è
verificato. Il guest può gestire tale situazione in due modi: può mascherare la
notifica dell'evento e verificare periodicamente se ci sono messaggi pendenti,
oppure può utilizzare un meccanismo di notifica asincrono. Gli eventi possono
essere generati direttamente da Xen e possono rappresentare interruzioni hardware
o virtuali, o da altri guest. Gli eventi Xen possono essere utilizzati per creare un
canale tra guest in cui viaggiano informazioni riguardo, ad esempio, lo stato di
risorse condivise. La comunicazione tra due guest richiede 5 passi:
1. Il guest ricevente crea una nuova porta;
2. Il guest ricevente comunica l'esistenza della porta appena creata (ad esempio
attraverso XenStore);
3. Il guest mittente crea, a sua volta, una nuova porta qualora non ne avesse una
libera;
4. il guest mittente associa la porta precedentemente creata, ad ad una sorgente
di eventi (ad esempio, un IRQ fisico, un IRQ virtuale, o un porto di un altro
dominio); nel nostro caso la porta viene associata a quella creata sul lato
ricevente;
5. Solo a questo punto è possibile utilizzare il canale per la comunicazione di
eventi.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
54
2.2.5.3 Grant tables
Il modello basato sui segnali di Unix è ottimo per notifica veloce di eventi ma non è
sufficiente per costruire un meccanismo per la comunicazione tra processi (IPC). A
tale scopo esistono svariati approcci quali code di messaggi o ancora memoria
condivisa. In ambiente Xen la comunicazione interdominio avviene mediante le
“grant tables”. Si tratta di un meccanismo che prevede la condivisione di aree di
memoria tra domini. La tecnica dello “split driver” è realizzata proprio grazie alle
grant tables. Come accennato nei precedenti paragrafi, un dominio non privilegiato
s’interfaccia con un driver virtuale che a sua volta comunica, tramite memoria
condivisa, con il driver reale residente nel dom0.
Ciascun dominio ha la propria grant table; è una struttura dati condivisa con Xen
che permette a ciascun dominio di comunicare all'hypervisor quali permessi
possiedono gli altri domini sulle sue pagine. Le voci presenti nella grant table, note
come “grant reference”, sono degli interi che indicano i privilegi che un
concessionario mette a disposizione sull'area di memoria che intende condividere.
Un “grant reference” incapsula, inoltre, altre informazioni che esentano un dominio
dal conoscere il reale indirizzo della pagina condivisa. Questo offre la possibilità di
condivisione della memoria anche tra domini “fully virtualized”.
2.2.5.4 XenStore
XenStore è uno spazio di memorizzazione d’informazioni condivise tra i domini. È
pensato per le notifiche di informazioni di configurazione e di stato piuttosto che per
grandi trasferimenti di dati. Ogni dominio possiede una sorta di pseudo file system
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
55
analogo a procfs dei sistemi Unix like. Quando sono modificati i valori dello store, i
driver corrispondenti vengono avvertiti. Lo store conta tre percorsi principali:
- /vm – memorizza le informazioni di configurazione dei domini, ad
esempio il nome simbolico del dominio (ssidref), il numero di cpu dedicate
(vpcpu_avail), la memoria disponibile (memory). Altri tipi di informazioni
presenti riguardano il comportamento da assumere in corrispondenza di
un determinato stato della macchina, cioè: le azioni da eseguire quando il
dominio riceve una richiesta di reboot (destroy o restart), le azioni da
svolgere quando il dominio riceve una richiesta di halt (destroy o restart)
e le azioni da compiere quando il dominio va in crash (destroy o restart);
- /local/domain – contiene le informazioni sul dominio correntemente in
esecuzione.
- /tool – contempla informazioni riguardo I tool di supporto.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
56
2.2.6 Approfondimento sulle hypercall
Nei seguenti paragrafi è proposta una descrizione approfondita di alcune hypercall,
oggetto dei test descritti nei capitoli successivi.
2.2.6.1 Virtual CPU setup
In fase di avvio, il sistema operativo ospite ha bisogno di inizializzare la/le CPU
virtuali su cui dovrà eseguire. Quest’operazione include la configurazione
dell’“Interrupt Descriptor Table” virtuale in modo che ciascun sistema operativo
guest possa gestire gli interrupt, i page fault, etc. Un’atra operazione compiuta in
fase di avvio dal sistema operativo guest è il settaggio di alcune callback utilizzate
da Xen per la notifica degli eventi.
HYPERVISOR_set_callbacks(unsigned long event_selector,
unsigned long event_address,
unsigned long failsafe_selector,
unsigned long failsafe_address)
Figura 2.16 – HYPERVISOR_set_callbacks
L’hypercall di figura 2.16 registra una callback “ordinaria” e una “fail-safe” per
l’elaborazione degli eventi. Il valore di event_address specifica l’indirizzo della
routine di gestione e dispatching dell’evento. Il failsafe_address specifica, invece,
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
57
un diverso entry point che è usato solo se, a causa di un guasto, Xen tenta di
utilizzare la callback “ordinaria”.
Una volta inizializzate le callback, il sistema operativo guest può installare la IDT
virtuale con la seguente hypercall:
static inline int
HYPERVISOR_set_trap_table(struct trap_info *table)
{
return _hypercall1(int, set_trap_table, table);
}
Figura 2.17 – HYPERVISOR_set_trap_table
Ogni voce nella tabella include il numero del vettore dell’eccezione con il
corrispondente entry point della routine di gestione. In ambiente Xen, la maggior
parte dei sistemi operativi ospiti possono utilizzare gli stessi handler come
avverrebbe in presenza di hardware reale.
2.2.6.2 Scheduling e timer
Lo scheduling dei domini in Xen avviene in accordo a parametri impostati nel Dom0.
Tuttavia, un dominio può esplicitamente scegliere di controllare alcuni dei suoi
comportamenti attraverso la seguente hypercall:
HYPERVISOR_sched_op(int cmd, void *arg)
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
58
Mediante la sua invocazione viene effettuata una richiesta di un’operazione di
scheduling all’hypervisor. A titolo di esempio si riporta in seguito l’elenco di valori
che il parametro cmd può assumere:
- SCHEDOP_yeld: rilascia la CPU ad un altro dominio runnable;
- SCHEDOP_block: rimuove il dominio chiamante dalla coda dei domini
runnable e porta tale dominio nello stato di sleep;
- SCHEDOP_shutdown: ferma l’esecuzione del dominio chiamante
notificando l’evento al controller;
- SCHEDOP_poll: interroga una serie di event-channel ports restituendo
il controllo al chiamante non appena c’è un evento pendente;
- SCHEDOP_remote_shutdown: l’utilizzo principale è quello di
interpretare richieste di arresto nonché le motivazioni da parte di domini
fully virtualized;
In particolare, nel caso di SCHEDOP_shutdown, le motivazioni dello spegnimento
del dominio possono essere:
- il dominio si spegne “normalmente”;
- il dominio si riavvia;
- il dominio si autosospende;
- il dominio va in crash;
- il watchdog timer è scaduto.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
59
2.2.6.3 Gestione delle tabelle delle pagine
Dato che un sistema operativo guest ha privilegi di sola lettura alle sue tabelle delle
pagine, Xen deve essere coinvolto ogni volta che si effettua una cambiamento in
memoria. La seguente hypercall:
HYPERVISOR_mmu_update(struct mmu_update *req, int count,
int *success_count, domid_t domid)
consente di modificare le tabelle delle pagine, effettuare il flush del TLB, installare
una nuovo puntatore base di una tabella delle pagine, e molto altro.
Ad esempio, nel caso di aggiornamento della tabella delle pagine di un dominio,
count rappresenta il numero di modifiche da effettuare in “batch”,
success_count indica il numero di aggiornamenti effettuati con successo, domid
identifica il dominio che richiede tali aggiornamenti.
struct mmu_update {
uint64_t ptr; /* Machine address of PTE. */
uint64_t val; /* New contents of PTE. */
};
Figura 2.18 – struct mmu_update
Ciascun elemento di req [] contiene un puntatore e un valore (figura 2.18). In
particolare, gli ultimi 2 bit meno significativi di ptr permettono di discriminare il tipo
aggiornamento che s’intende effettuare:
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
60
- MMU_NORMAL_PT_UPDATE aggiorna una page directory o una page
table identificata da val;
- MMU_MACHPHYS_UPDATE aggiorna l’entry di una tabella machine-to-
physical;
- MMU_EXTENDED_COMMAND si occupa di eseguire operazioni che
coinvolgono il dispositivo MMU. Tali operazioni sono: l’aggiornamento del
registro cr3, il flushing della cache, l’installazione di una nuova Local
Descriptor Table, etc.
HYPERVISOR_mmu_update consente di effettuare più aggiornamenti in un sol
colpo; tuttavia, esistono casi in cui è richiesta la modifica di una sola entry in una
data tabella delle pagine. In tale circostanza è possibile ricorrere a:
static inline int
HYPERVISOR_update_va_mapping(unsigned long va, pte_t new_val,
unsigned long flags)
che aggiorna una voce della tabella delle pagine va al valore val. Come la
hypercall citata in precedenza, Xen si accerta che la modifca richiesta sia sicura
prima di applicarla. Il parametro flags consente, inoltre, di definire il tipo di flush
della TLB da compiere in seguito.
Infine, se un dominio ha sufficienti privilegi, può, occasionalmente, manipolare le
pagine degli altri domini utilizzando:
HYPERVISOR_update_va_mapping_otherdomain
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
61
Quest’operazione privilegiata è effettuata, ad esempio, dal driver virtuale di back-
end per trasferire dati all’altra sezione del driver (front-end) presente in un altro
dominio.
2.2.6.4 Comunicazione interdominio
Xen mette a disposizione un meccanismo asincrono di notifica servendosi degli
event channels. Ciascun dominio ha un set di porti che possono essere associati ad
una sorgente di eventi (event source) quali una IRQ virtuale, una IRQ fisica o una
porta di un altro dominio. La comunicazione tra due domini avviene a partire dalla
seguente hypercall:
HYPERVISOR_event_channel_op(int cmd, void *arg)
Dove il valore di cmd discrimina sette possibili operazioni:
- alloc_unboud consente ad un dominio di istituire un porto da utilizzare
successivamente per la comunicazione con un altro dominio;
- bind_virq effettua il bind di un porto locale con un IRQ virtuale;
- bind_pirq effettua il bind di un porto locale con un IRQ fisico;
- bind_interdomain costruisce un canale di comunicazione, nel gergo
di Xen event cannel, tra due domini; in generale, il dominio effettua
questa operazione solo dopo aver creato un porto “unbound” mediante
alloc_unbound;
- close chiude un canale tra due domini;
- send invia un evento all’altro capo del canale;
- status determina lo stato corrente del porto locale.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
62
2.2.6.5 HYPERVISOR_multicall
La trattazione delle hypercall termina con la discussione di quella che sarà, più di
tutte, interessata nella fase di testing. Si tratta di HYPERVISOR_multicall:
HYPERVISOR_multicall(void *call_list, int nr_calls)
Con questa hypercall è possible eseguire in “batch” un certo numero di hypercall,
nr_calls, in precedenza caricate in un buffer puntato da call_list. Questo
tipo di ottimizzazione si rivela particolarmente utile nei casi di context switch, dove,
la nuova macchina in esecuzione richiede una serie operazioni preliminari svolte
dalle singole hypercall. Un meccanismo analogo è previsto con le operazioni di
aggiornamento delle tabelle delle pagine che possono essere eseguite in lotti
attraverso HYPERVISOR_mmu_update. Questo stratagemma va a beneficio delle
prestazioni poiché richiede meno transizioni della CPU (dal ring 1 o 3 a 0) rispetto
all’approccio di eseguire le stesse operazioni attraverso singole hypercall.
struct multicall_entry {
unsigned long op , result ;
unsigned long args [ 6 ] ;
} ;
Figura 2.19 – struct multicall_entry
La figura 2.19 mostra il formato degli elementi dell’array puntato da call_list. Il
campo op detiene il numero dell’hypercall (codice operativo), cioè il valore che
conterrebbe il registro EAX nel caso di hypercall singola; result conterrà il valore
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
63
di ritorno prodotto dall’esecuzione dell’hypercall op, che contrariamente al caso di
singole chiamate non andrà a sovrascrivere il registro EAX; args detiene gli
argomenti previsti per l’hypercall op. Inoltre, è importante ricordare che,
diversamente dal caso di singole hypercall, un guest può essere interrotto nel
mezzo dell’esecuzione di una multicall. Il discorso sul miglioramento delle
prestazioni apportate da questo approccio va a vantaggio dei domini
paravirtualizzati dato che simili meccanismi non sono previsti dall’altra tipologia di
macchine virtuali supportate da Xen (HVM domain).
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
64
Capitolo 3
Tool di fault injection
Il contributo principale del presente lavoro di tesi è un tool per l'iniezione di guasti
su macchine che eseguono in ambiente virtualizzato Xen. Esso comprende una
patch da applicare al kernel e una serie di script di supporto. Come già accennato
nei capitoli precedenti, si vuole sperimentare una tecnica di iniezione di guasti la cui
finalità è quella di consentire all'utente l’applicazione di diverse tipologie di fault
nello strato software che interfaccia il sistema operativo ospite con l’infrastruttura di
virtualizzazione. Tale interfaccia è costituita da funzioni note come “hypercall” il cui
comportamento è analogo a quello delle “system call”.
3.1 Scenario di riferimento
In figura 3.1 è rappresentato un tipico scenario in cui sono presenti tre domini:
1. Un dominio privilegiato (dom0) presso cui esegue il modulo front-end del
tool;
2. Un dominio target non privilegiato (al centro) su cui eseguono il modulo di
back-end del tool e il modulo che sottopone tale dominio a carichi di lavoro
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
65
allo scopo di simulare condizioni reali di funzionamento;
3. Un terzo dominio non privilegiato su cui esegue, eventualmente, un certo
workload.
In rosa è evidenziato il punto di iniezione su cui agisce il tool.
Figura 3.1 - Scenario di riferimento
Le caratteristiche principali del tool sono:
1. Possibilità di compiere un profiling preventivo volto all'individuazione delle
hypercall invocate con maggiore frequenza in modo da poter condurre, in
seguito, un’oculata campagna di fault injection;
2. Scelta del tipo di fault da iniettare con la possibilità di specificare parametri
quali probabilità e numero di occorrenze del fault in modo da poter simulare
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
66
molteplici condizioni di funzionamento;
3. Raccolta dei log generati dalla macchina sottoposta al test.
3.2 Architettura
Con riferimento alla figura 3.2, il tool si compone di diversi moduli aventi specifiche
funzioni:
Figura 3.2 - Architettura del tool
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
67
1. Controller, residente nel Dom0, adibito al lancio dello script di iniezione e al
monitoraggio dello stato della macchina target predisponendone,
eventualmente, il riavvio in caso di crash;
2. Monitor, residente nel Dom0, raccoglie i log del DomU a valle
dell'applicazione del singolo fault;
3. Data collector, residente nel Dom0, “studia” i dati raccolti dal monitor
producendo dei report;
4. Workload Generator; genera carichi di lavoro sulla macchina target, ossia un
insieme di input assimilabile a quello che si registra in un’esecuzione tipica;
è possibile eseguire questo modulo sia sulla macchina target che su quella
che inietta i fault.
5. Fault injector, esegue nella macchina target; è il modulo responsabile
dell'iniezione dei fault inviati dal modulo controller tramite connessione ssh;
6. Profiler; si tratta di uno script che si occupa di individuare le hypercall
invocate con maggiore frequenza. I report da esso prodotti costituiscono la
base di partenza per la conduzione dalla campagna di fault injection;
7. Target system, sistema da testare; nella presente implementazione, si tratta
di una macchina virtuale con kernel Linux.
Tornando alle funzionalità offerte dal tool è di seguito riportata una descrizione più
accurata di ciascuna di esse.
3.3 Profiling
La creazione di una campagna di test passa attraverso un'attività di studio la cui
finalità è quella di individuare i punti in cui verranno applicati i fault. Con riferimento
allo scenario proposto nei paragrafi precedenti, un possibile approccio consiste
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
68
nell'individuare le hypercall invocate con maggiore frequenza avendo sottoposto il
sistema ad uno specifico workload. A tal proposito il tool, è corredato di uno script
realizzato con SystemTap che adempie i suddetti scopi. SystemTap è uno
strumento di analisi e probing che permette di studiare e monitorare in dettaglio le
attività del sistema operativo (in particolare, il kernel). Fornisce informazioni simili a
quelle generate da altri strumenti come netstat, ps, top e iosat introducendo ulteriori
opzioni per il filtraggio delle informazioni raccolte. A corredo di SystemTap vi è
un'utility denominata stap che permette di eseguire degli script con lo scopo di
monitorare determinate parti del sistema operativo in oggetto. Tali script saranno poi
tradotti in codice C e compilati in un modulo che verrà caricato nel kernel per
svolgere le operazioni richieste. Le operazioni da svolgere in questo caso
consistono nell'individuare quali hypercall sono maggiormente invocate. Avendo
sottoposto il sistema target a un determinato workload si lanciano in sequenza degli
script SystemTap ciascuno dei quali produce in output il numero di volte, nell'arco
del periodo di osservazione, in cui viene invocata la hypercall i-esima. Si riporta di
seguito il codice dello script che legge l'hypercall i-esima da un file (hypercall.txt) e
richiama lo script di probing di SystemTap passando come argomento la suddetta
hypercall. Lo script SystemTap effettua il probing dell'hypercall per un intervallo di
60 secondi al termine del quale scrive su un file il numero di volte in cui la funzione
in questione è stata richiamata. Avendo, a questo punto, a disposizione informazioni
più dettagliate riguardo il comportamento del sistema è possibile realizzare un
testbed accurato.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
69
#!/bin/bash
while read line
do
echo "${line}";
var="${line}"
echo "stap -v probe_generic.stp" $var
stap -v probe_generic.stp $var >> generic.txt
done < hypercall.txt
Figura 3.3 - Script profiling
global syscalllist
probe kernel.function(@1) //@1 raccoglie l'hypercall
corrente di cui fare probing
{
syscalllist++
}
probe timer.ms(60000) {
printf("%s %d\n",@1, syscalllist)
exit()
}
Figura 3.4 - Script SystemTap profiling
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
70
3.4 Scelta dei fault
La seconda fase della sperimentazione consiste nell’applicazione dei fault alle
hypercall target individuate nel passaggio precedente. A tal proposito il tool prevede
uno script da eseguire nella macchina da testare. Detto script ha il compito di
passare al filesystem virtuale, debugfs, i parametri caratterizzanti il singolo fault. Il
presente tool, infatti, nasce per estensione del tool d’iniezione di guasti già presente
in ambiente Linux. Questo tool, consente di iniettare uno specifico fault offrendo la
possibilità di stabilire le modalità temporali e spaziali con cui deve verificarsi il
guasto; è possibile impartire tali istruzioni direttamente dallo spazio utente
servendosi del file system virtuale.
Mantenendo intatto l'approccio originario, il tool di iniezione guasti è stato arricchito
in modo da poter applicare fault anche in ambiente virtualizzato. In particolare è
stato aggiunto un nuovo tipo di guasto (denominato hfail) al quale sono stati
associati nuovi parametri caratterizzanti. Tali parametri sono:
1. hypercall specifica l'id dell'hypercall a cui si vuole applicare il guasto;
nel caso venga indicata una hypercall non contemplata dal tool il fault
non avrà luogo;
2. posizione indica su quale parametro si vuole applicare il guasto.
Anche in questo caso il tool esegue un controllo finalizzato a stabilire
la fattibilità del test;
3. tipo_iniezione specifica il tipo di guasto che s'intende applicare. In
questo caso l'utente è chiamato a scegliere fra tre possibili alternative:
3.1 iniezione random prevede la modifica dell’argomento target con un
valore calcolato in maniera casuale;
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
71
3.2 iniezione con tecnica del bit flip effettua il complemento di un bit la
cui posizione è indicata da parametro_iniezione;
3.3 iniezione user defined. In questo caso l'utente è chiamato a
specificare anche il valore che si vuole sostituire a quello
originariamente detenuto dall'argomento target;
4. parametro_iniezione assume un significato diverso a seconda del
valore di tipo_iniezione (si rimanda al punto 3).
A questo punto si possiedono informazioni sufficienti per adoperare il tool; infatti,
volendo simulare l'alterazione del quinto bit dell'argomento numero uno
dell'HYPERVISOR_sched_op (numero 29), basterà richiamare lo script con i
seguenti parametri:
– hypercall = 29
– posizione = 1
– tipo_iniezione = 1 (bit flip)
– parametro_iniezione = 5.
E, avvalendosi delle caratteristiche native del tool l'utente può inoltre specificare:
– la probabilità con cui si verifica il guasto (un intero compreso tra 0 e 100);
– il numero di occorrenze del fault;
– l'intervallo temporale tra due guasti consecutivi.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
72
Figura 3.5 - Esempio iniezione HYPERVISOR_sched_op
Alle suddette caratteristiche sono state aggiunte altre in modo da consentire
l'iniezione mirata di una specifica hypercall ampiamente trattata nel capitolo
precedente: si tratta di HYPERVISOR_multicall. Questa hypercall si occupa di
eseguire “in batch” un certo numero di hypercall raccolte in precedenza in un buffer.
Quindi, a complemento dei parametri precedentemente illustrati, si aggiungono:
– mhypercall specifica la posizione dell'hypercall nel buffer a cui s'intende
applicare il guasto;
– mposizione individua il campo dell'hypercall che s'intende iniettare. È
possibile applicare il guasto al codice operativo, al valore di ritorno o ad uno
dei sei parametri previsti;
– idhypercall individua l'hypercall che si vuole iniettare. Se a seguito di un
controllo non dovesse risultare alcuna hypercall con detto id il guasto non
avrà luogo.
Lo scenario riportato in figura 3.6 illustra il caso in cui il fault viene iniettato
nell’argomento zero della hypercall numero tre
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
73
Figura 3.6 - Fault injection su HYPERVISOR_multicall
3.5 Raccolta dei risultati
Terminata la fase di sperimentazione, l’utente avrà a disposizione una raccolta di
log, generati dal kernel della macchina testata e inviati al modulo controller tramite
connessione UDP. A tal uopo, è anche possibile servirsi della porta seriale virtuale.
3.6 Dettagli implementativi
In questo paragrafo si riporta una descrizione accurata dei vari moduli che
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
74
compongono il tool.
3.6.1 Modifiche al codice sorgente
Si tratta di un patch da applicare al kernel che prevede la creazione del nuovo fault
all’avvio del sistema e l’applicazione dello stesso in base alla configurazione fatta
dall’utente. Il tutto è stato ottenuto mediante la creazione di un nuovo file sorgente,
initfault.c, e la modifica del file hypercall.h.
La principale mansione di initfault.c è quella di inizializzare il guasto all’avvio del
sistema con parametri di default mediante la creazione di una directory nel file
system virtuale in cui si configurano le caratteristiche del fault stesso. Tra le altre
cose, initfault.c contiene funzioni da richiamare prima dell’applicazione del fault atte
a validare e ad impostare il guasto in linea con le scelte operate dall’utente.
In seguito è riportata l’implementazione in linguaggio c della funzione
should_fail_xen in cui è valutata la fattibilità del fault e in caso affermativo si
procede con il set up del guasto che sarà successivamente iniettato.
/* -1: non applico fault-injection
1: non applico fault-injection ma non chiamo la hyper-
call in questione
2: fault-injection - random (par1 = parametro da iniettare
par2 = any)
3: fault-injection - bit flip (par1 = parametro da
iniettare par2 = posizione bit all'interno del parametro)
4: fault-injection - user defined (par1 = parametro da
iniettare par2 = valore da iniettare)
*/
1 int should_fail_xen(int argomenti, int* par1, int* par2)
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
75
2 {
3 if (params.posizione < 0 || params.posizione>argomenti)
4 return -1;
5 if (params.posizione==0)
6 return 1;
7
8 *par1=params.posizione;
9 *par2=params.parametro_iniezione;
10
11 if (params.tipo_iniezione == 0)
12 return 2; //random
13 if (params.tipo_iniezione == 1)
14 return 3; //bit flip
15 if (params.tipo_iniezione == 2)
16 return 4; //user defined
17
18 return -1;
19 }
Figura 3.7 - Codice should_fail_xen
Un caso particolare accade qualora s’intende applicare il guasto alla funzione
HYPERVISOR_multicall ampiamente descritta nei paragrafi precedenti. In questo
scenario, infatti, prima dell’applicazione del guasto è necessario completare il suo
setup con dei parametri aggiuntivi. Tale compito è svolto dalla funzione
get_other_params la cui implementazione in linguaggio C è riportata in seguito.
Anche in questo caso l’iniezione del fault è vincolata a determinate condizioni (riga
7).
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
76
1 int get_other_params(int* mpar1, int* mpar2, int *mpar3,
2 int nr_calls)
3 {
4 *mpar1 = params.mhypercall;
5 *mpar2 = params.mposizione;
6 *mpar3 = params.idhypercall;
7 return (params.mhypercall<= nr_calls &&
8 params.mposizione<8);
9 }
Figura 3.8 - Codice get_other_params
Le altre modifiche hanno interessato il file hypercall.h
(arch/x86/include/asm/xen/hypercall.h). È qui, infatti, presente l’implementazione di
ciascuna hypercall. Come descritto nel capitolo precedente, l’esecuzione di una
hypercall passa attraverso una funzione wrapper che si occupa di richiamare una
delle macro apposite in linea con il numero di parametri previsti dalla funzione che
s’intende eseguire.
Riassumendo è possibile schematizzare ciò che svolge una generica hypercall in
tre punti come riportato in figura 3.9.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
77
HYPERVISOR_x (* parametri *)
{
1. setup parametri
2. verifica hypercall da invocare
3. chiamata macro hypercall decisa al punto 1
}
Figura 3.9 - Hypercall generica
Per ciascuna di queste hypercall, sono state introdotte delle modifiche al codice in
modo da supportare l’iniezione del fault. Tali modifiche prevedono la sostituzione
del blocco corrispondente al punto 1 di Figura 3.9 con un blocco che, nel
complesso, si espleta nei seguenti punti:
HYPERVISOR_x (* parametri *)
{
1. setup parametri
1a. verifica condizioni applicazione fault
1b. setup fault
1c. applicazione fault
2. verifica hypercall da invocare
3. chiamata macro hypercall decisa al punto 1
}
Figura 3.10 - Codice hypercall modificato
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
78
Dove l’esecuzione dei punti 1b e 1c è vincolata all’esito positivo del test del blocco
1a.
Come caso concreto è riportato di seguito il codice c di una delle hypercall
modificate.
1 static inline int
2 HYPERVISOR_multicall(void *call_list, int nr_calls)
3 {
4 int par1=0; //posizione parametro da iniettare
5 int par2=0; //dipende da par1
6 int mpar1=0; //posizione hypercall nel buffer da
7 //iniettare <= nr_calls
8 int mpar2=0;
9 int mpar3=0;
10 int res=0;
11 int found=0;
12 int inject = 0;
13 struct multicall_entry * m_entry;
14 if (hypercall_test(HYPERVISOR_multicallnr) &&
15 should_fail(&hfail_attr,1))
16 {
17 res = should_fail_xen(2, &par1, &par2);
18 if (par1==1) //voglio iniettare il primo parametro
19 {
20 inject = get_other_params(&mpar1, &mpar2,
21 &mpar3, nr_calls);
22 }
23 }
24 //1: non invoco l'hypercall
25 if (res==1)
26 {
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
79
27 return;
28 }
29
30 /*verifico se il parametro da iniettare è il nr. 1
31 o no.
32 mpar1 = 0 --> devo cercare l'hypercall mpar3
33 all'interno del buffer
34 mpar1 > 0 --> inietterà l'hypercall in
posizione 35 mpar1 */
36 if (mpar1==0)
37 {
38 int i =0;
39 while (i < nr_calls && !found)
40 {
41 m_entry = call_list+ i * sizeof(struct
42 multicall_entry);
43 if (m_entry->op == mpar3)
44 found=1;
45 i++;
46 }
47
48 }
49 else if (mpar1>0)
50 {
51 m_entry = call_list+ mpar1* sizeof(struct
52 multicall_entry);
53 found=1;
54 }
55
56
57 if (res==2) /* 2: fault-injection - random (par1 =
58 pos. parametro da iniettare par2 =any,
59 non considerato in questo caso)*/
60 {
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
80
61
62 if (inject && par1==1 && found==1)
63 {
64
65 if (mpar2==0) //inietto op
66 {
67 get_random_bytes(&m_entry->op,
68 sizeof(unsigned long));
69 }
70 if (mpar2==1) //inietto result
71 {
72 get_random_bytes(&m_entry->result,
73 sizeof(unsigned long));
74 }
75 if (mpar2>1 && mpar2<8) //inietto
76 //uno dei sei argomenti
77 {
78 get_random_bytes(&m_entry->args[mpar2-2],
79 sizeof(unsigned long));
80 }
81
82 }
83 if (par1==2)
84 {
85 get_random_bytes(&nr_calls, sizeof(int));
86 }
87 }
88 if(res==3) //3: fault-injection - bit flip (par1 =
89 //parametro da iniettare par2 = posizione
90 //bit all'interno del parametro)
91 {
92 if(inject && par1==1 && found==1)
93 {
94 if (mpar2==0) //inietto op
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
81
95 {
96 m_entry->op=flip(m_entry->op, par2);
97 }
98 if (mpar2==1) //inietto result
99 {
100
101 m_entry->result=flip(m_entry->result,
102 par2);
103 }
104 if (mpar2>1 && mpar2<8) //inietto uno dei
sei 105 //argomenti
106 {
107 m_entry->args[mpar2-2]=flip(m_entry->
108 args[mpar2-2], par2);
109 }
110 }
111 if(par1==2)
112 {
113 nr_calls = flip(nr_calls, par2);
114 }
115 }
116
117 if (res==4) //4: valore scelto dall'utente
118 {
119 if(inject && par1==1 && found==1)
120 {
121 if (mpar2==0) //inietto op
122 {
123 m_entry->op=par2;
124 }
125 if (mpar2==1) //inietto result
126 {
127 m_entry->result=par2;
128 }
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
82
129 if (mpar2>1 && mpar2<8) //inietto
130 //uno dei sei argomenti
131 {
132 m_entry->args[mpar2-2]=par2;
133 }
134 }
135 if(par1==2)
136 {
137 nr_calls = par2;
138 }
139
140 }
141
142 return _hypercall2(int, multicall, call_list, nr_calls);
143 }
144
Figura 3.11 - HYPERVISOR_multicall modificata
3.6.2 Modulo per l’iniezione dei guasti Sulla macchina target esegue un modulo, inject.sh, che si occupa di applicare il
guasto inviato dal modulo xen_test.sh via ssh. Come evidenziato in figura 3.12,
inject.sh scrive i parametri del guasto raccolti nel file system virtuale dal quale, una
volta stabilita la fattibilità del guasto, saranno prelevati dalla funzione
should_fail_xen.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
83
Figura 3.12 - Schema iniezione fault
Una configurazione alternativa prevede il lancio dello script inject.sh direttamente
dalla macchina target. Tale configurazione, tuttavia, limita fortemente l’usabilità del
tool; infatti, in caso di crash della macchina, l’utente dovrà predisporre il ravvio
manualmente. Si riporta, in seguito, un piccolo estratto dallo script in questione.
1 if [[ $hypercall =~ ^[0-9]+$ ]] && [ ! -z "$hypercall" ];
2 then
3 echo $hypercall > /sys/kernel/debug/hfail/hypercall
4
5 fi
Figura 3.13 - Configurazione del parametro “hypercall” del guasto
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
84
Per ogni parametro ammesso, inject.sh, dopo aver testato che si tratta di un
numero, lo pone nell’apposita cartella del file system virtuale (istruzione 3).
3.6.3 Iniettore DomU
Lo script xen_test.sh gira nel dominio privilegiato; come più volte accennato, il suo
compito è quello di prelevare dal file test.txt l’ennesimo caso test e di inviarlo allo
script inject.sh. È responsabile, inoltre, del monitoraggio dello stato della macchina
target e della raccolta dei log generati dal kernel.
#!/bin/bash
#Funzionalità dello script:
# 1. Monitora lo stato della macchina target predisponendo un
riavvio nel caso di crash
# 2. Avvia lo script di workload sul server Apache
# 3. Legge i test da un file e li passa tramite ssh ad uno script di
iniezione residente sulla macchina target
# 4. Stampa a video un breve report del'iniezione in corso
test=0 #contatore test
notest=0 #indica se si effettua una test o meno in base ai parametri
forniti in input
multi=0 #1 se viene chiamata una HYPERVISOR_multicall
par1=0 #1 se il paramentro oggetto del fault e par1
if [ "x$1" == "x" ]
then
echo "Usage: $0 {test file}"
exit 1
fi
#lancio per la prima volta il dominio che intendo testare
state=`xm domstate Ubuntu1216`
if [[ $state == "" ]]
then
echo "domain not active...need to start"
xm create /etc/xen/Ubuntu1216.cfg
#echo "start Domanin Ubuntu1216..."
sleep 30;
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
85
else
echo "domain already active..."
fi
#avvio workload server apache
ssh -n -f 192.168.122.18 'cd httpref_fi; ./work_gen.sh' >> httpref.txt
while read line
do
if [[ ${line} == *,* ]] && [[ ! ${line} == //* ]];
then
FILE="report/dmesg"$test".txt"
ssh -n -f 192.168.122.18 dmesg > $FILE
sleep 5;
if [[ -s $FILE ]]
then
echo "Target domain active..."
else
echo "Host unreachable...need to restart";
xm reset Ubuntu1216
echo "Restart Ubuntu 1216";
sleep 30;
#avvio workload server apache
ssh -n -f 192.168.122.18 'cd httpref_fi;
./work_gen.sh' >> httpref.txt
sleep 10;
fi
#estraggo i parametri di un singolo test e li metto in un
array
arr=$(echo ${line} | tr "," "\n")
index=0
for x in $arr
do
par[$index]=$x
let "index += 1"
done
#applicazione test
#h#l#i#a#m#n#t#p
let "test += 1"
echo applicazione test nr. $test
echo "data "`date`" ("` date +%d_%m_%Y_%H_%M_%S `")"
case ${par[0]} in
13) echo "Iniezione HYPERVISOR_multicall"
multi=1 ;;
29) echo HYPERVISOR_sched_op ;;
24) echo HYPERVISOR_vcpu_op ;;
32) echo HYPERVISOR_event_channel_op ;;
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
86
0) echo HYPERVISOR_set_trap_table ;;
*) echo No test
notest=1
esac
if [ "$notest" -eq 0 ]
then
case ${par[1]} in
0) echo non richiamo hypercall ;;
1) if [ "$multi" -eq 1 ]
then
echo "Oggetto iniezione: HYPERVISOR_multicall"
if [ ${par[4]} -eq 0 ]
then
echo "Hypercall da iniettare nr: "
${par[6]}
else
echo "Posizione Hypercall da iniettare: "
${par[4]}
fi
case ${par[5]} in
0) echo " --->Inietto il codice operativo"
;;
1) echo " --->Inietto il valore di ritorno"
;;
*) let "arg=par[5]-2"
echo " --->Inietto l'argomento: "
$arg
esac
fi ;;
*) echo "Iniezione sul parametro" ${par[1]}
esac
if [ ${par[1]} -ne 0 ]
then
case ${par[2]} in
0) echo "Tipo iniezione: random" ;;
1) echo "Bit flip sul bit nr. " ${par[3]} ;;
2) echo "Iniezione user defined valore: " ${par[3]}
esac
fi
echo "Probabilità fault: " ${par[8]} "%"
echo "Numero di occorrenze fault: " ${par[7]}
fi
ssh -n -f 192.168.122.18 ./inject.sh -h ${par[0]} -l
${par[1]} -i ${par[2]} -a ${par[3]} -m ${par[4]} -n ${par[5]} -d
${par[6]} -t ${par[7]} -p ${par[8]}
echo done...
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
87
notest=0
multi=0
par1=0
sleep 60;
echo -----------------------------
fi
done < $1
echo
echo fine
Figura 3.14 - xen_test.sh
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
88
Capitolo 4
Sperimentazioni
In questo capitolo sono presentati due possibili scenari di applicazione del tool.
Nel primo caso, ci si servirà per valutare l'affidabilità di una certa macchina. Si
tratta di una macchina con kernel Linux operante in ambiente virtualizzato in
presenza di Xen hypervisor. Il secondo scenario prevede un'architettura più
complessa rispetto al caso precedente: infatti, si avrà a che fare con due
macchine virtuali di cui, una, sarà sottoposta agli stessi test previsti per il primo
caso mentre dell'altra macchina si analizzeranno affidabilità e prestazioni allo
scopo di valutare il grado di isolamento garantito dall'hypervisor Xen.
4.1 Profiling
Il tool, prima dell’avvio dell’iniettore, offre la possibilità scegliere in maniera
oculata le hypercall che saranno interessate, in seguito, dai guasti. Un possibile
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
89
criterio di scelta è di individuare le hypercall invocate con maggiore frequenza
una volta sottoposto il dominio target a un certo workload. Tale workload consiste
nell’effettuare richieste http ripetute alla macchina in oggetto. Il tutto è ottenibile
mediante lo script di profiling, profiler.sh, congiuntamente allo script per la
generazione del workload work_gen.sh (per i dettagli si rimanda al capitolo
precedente).
Le hypercall invocate con maggiore frequenza sono:
HPERCALL Frequenza
HYPERVISOR_multicall 44.9%
HYPERVISOR_sched_op 16.4%
HYPERVISOR_vcpu_op 22.1%
HYPERVISOR_event_channel_op 1.9%
Figura 4.1 – Frequenza di invocazione principali hypercall
Una volta individuate le hypercall “target” è possibile procedere con la seconda
fase.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
90
4.2 Iniezione dei guasti
La seconda fase, prevede l’iniezione dei guasti alle hypercall individuate al
passaggio precedente nonché la raccolta dei log generati dal sistema.
Il primo scenario di test prevede due domini in esecuzione:
1. Il primo, il Dom0, è un dominio con sistema operativo Ubuntu 12.04 (kernel
3.2.0) su cui esegue lo script che si occupa del controllo della campagna di
testing;
2. Un secondo dominio, DomU, rappresenta la macchina da testare.
Come accennato nell’introduzione, il ruolo del Dom0 è monitorare lo stato di
avanzamento dei test nonché le condizioni della macchina target (crash, hang).
Queste mansioni sono svolte da un unico modulo, xen_test.sh. Per poter meglio
comprendere le funzionalità di tale modulo è di seguito riportata la sequenza di
passi necessaria per l’applicazione di un singolo caso di test:
1. Per prima cosa, la macchina target deve essere avviata; tale compito è
eseguito attraverso il comando:
xm create /etc/xen/Ubuntu1216.cfg
dove Ubuntu1216 è il nome della macchina che sarà sottoposta ai test nelle fasi
successive;
2. Completato l’avvio della macchina target, lo script prevede l’avvio del web server
Apache sul domU. Il tutto avviene, tramite connessione ssh attraverso il
comando:
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
91
ssh -n -f $HOST `service apache2 restart`
dove HOST indica l’indirizzo ip della macchina target;
Figura 4.2 – Avvio server web Apache
3. A questo punto, è possibile avviare un workload allo scopo di simulare
condizioni reali di funzionamento del DomU. Detto workload, consiste nel
generare continue richieste http al web server Apache. L’output di questo script
viene salvato su un file, httperf.txt e costituirà uno dei report utili nella fase di
analisi dei risultati;
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
92
Figura 4.3 – Avvio workload
4. La quarta fase consiste nell’iniezione del test i-esimo, prelevato da un file test.txt
che contiene la lista dei test che s’intende condurre. Tale test viene inviato
tramite connessione ssh ad uno script sul domU, inject.sh, che provvede
all’iniezione vera e propria. Il tutto avviene mediante il comando:
ssh -n -f $HOST ./inject.sh -h ${par[0]} -l ${par[1]} -i
${par[2]} -a ${par[3]} -m ${par[4]} -n ${par[5]} -d
${par[6]} -t ${par[7]} -p ${par[8]}
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
93
Figura 4.4 – Iniezione guasto
5. Contestualmente alla fase di iniezione, viene avviata anche una fase di raccolta
dei log generati dal kernel della macchina target. Tali log sono inviati al Dom0
tramite connessione UDP, dal modulo netconsole che esegue sul domU. Nel
Dom0 uno script si mette in ascolto e trasferisce su un file, test_xen.txt, tutto
quello che riceve dal suddetto porto.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
94
Figura 4.4 – Acquisizione log
Le fasi appena citate, vanno ripetute per ciascun test. Inoltre al termine di ogni test,
viene effettuato un controllo dello stato della macchina target, predisponendo un
riavvio in caso di anomalie.
4.3 Scelta delle hypercall
I casi di test considerati in questo primo scenario hanno interessato un’unica
hypercall: hypervisor_multicall. I motivi che hanno portato a tale scelta sono due:
1. L’hypercall in questione risulta, a valle della fase di profiling, tra quelle
maggiormente invocate;
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
95
2. Come riportato nei capitoli precedenti, tra le tante hypercall, hypervisor_multicall
è da considerarsi “particolare”, poiché una sua invocazione consente di eseguire
in “batch” più hypercall.
Sono stati individuati 50 casi di test, ciascuno dei quali ripetuto 8 volte. Il tutto è
stato eseguito in 11 ore circa.
4.4 Raccolta dei risultati
In questo paragrafo si illustrano strategie di raccolta dei risultati applicate a
ciascuno dei due test previsti.
4.4.1 Risultati esperimento uno
A conclusione di ogni test il tool genera i seguenti file, che saranno esaminati
nell'ultima fase di analisi dei risultati. Tali file sono:
1. xen_test.txt contiene la descrizione di ciascun test iniettato e i log generati
dal kernel a seguito della sua applicazione. Esistono casi in cui l'applicazione di un
test non ha prodotto alcun log; le motivazioni possibili sono due e concettualmente
opposte:
- il test non ha provocato il fallimento dell'hypercall interessata. Ad esempio,
nel caso dell'HYPERVISOR_multicall la corruzione di un'area di memoria,
all'interno del buffer multicall_entry, non utilizzata nell'esecuzione corrente
non ha alcun effetto;
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
96
- il test provoca un crash immediato del sistema. Questa condizione inibisce il
modulo netconsole di compiere il proprio lavoro.
È proposto in figura, lo stralcio del file in questione in cui è riportata una descrizione
dettagliata del test, un timestamp (utile nelle analisi successive) e i log raccolti a
seguito della sua applicazione:
Target domain active...
applicazione test nr. 2
data dom 17 feb 2013, 20.00.10, CET (17_02_2013_20_00_10)
Iniezione HYPERVISOR_multicall
Oggetto iniezione: HYPERVISOR_multicall
Hypercall da iniettare nr: 3
--->Inietto il codice operativo
Bit flip sul bit nr. 4
Probabilità fault: 100 %
Numero di occorrenze fault: 4
done...
kernel logs test 2
[ 100.472066] 1 multicall(s) failed: cpu 0
[ 100.478103] 1 multicall(s) failed: cpu 0
[ 100.482483] 1 multicall(s) failed: cpu 0
[ 100.488104] 1 multicall(s) failed: cpu 0
Figura 4.4 – Spaccato file test_xen.txt
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
97
2. httperf.txt contiene l'output prodotto dall'esecuzione di httperf. Le
informazioni in questo file sono utili sia per verificare l'esito di ciascuna richiesta
http, sia per eseguire un'analisi prestazionale. Infatti, al termine di ciascuna
sequenza di richieste (se ne eseguono 50 alla volta), vengono generati dei report
utili per le analisi successive. Al termine della campagna d’iniezione, uno script,
dimensione_httperf.sh, genera in output, per ogni test, la dimensione del file
httperf.txt associata. Questa informazione, può essere utile a discriminare casi di
test che hanno prodotto anomalie a livello applicativo da quelli innocui. Infatti, un
test che ha arrecato danni a livello applicativo, può comportare un rallentamento dei
tempi di risposta delle richieste http e quindi un throughput inferiore rispetto ai casi
nominali.
3. access.log e error.log. Se il test i-esimo si conclude senza un crash o riavvio
forzato della macchina è prevista la raccolta dei log prodotti da Apache nel periodo
di applicazione del suddetto test. Queste informazioni, congiuntamente a quelle
riportate in httperf.txt, consentono di valutare l'esito di ciascuna richiesta effettuata
al server Apache.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
98
4.4.1.1 Panoramica
Prima di procedere con la descrizione dei risultati si danno alcuni cenni circa i criteri
di classificazione stabiliti. A valle dell'applicazione di ciascun guasto si può
verificare una delle seguenti condizioni:
1. crash;
2. restart;
3. hang;
4. workload failure;
4.4.1.1.1 Crash
Si tratta di uno scenario in cui il dominio si riavvia automaticamente a seguito
dell'iniezione. Tale condizione è rilevata nei log prodotti da Xen e ricondotta allo
specifico test responsabile mediante una procedura manuale; infatti, nei log prodotti
da Xen l'informazione relativa al crash di un dominio è associata ad un timestamp.
[2013-02-17 20:37:30 1248] WARNING (XendDomainInfo:2061)
Domain has crashed: name=Ubuntu1214p id=12
Figura 4.6 – Messaggio contenuto nel log di Xen relativo ad un crash di un dominio
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
99
Target domain active...
applicazione test nr. 39
data dom 17 feb 2013, 20.37.29, CET (17_02_2013_20_37_29)
Iniezione HYPERVISOR_multicall
Iniezione sul parametro 2
Bit flip sul bit nr. 10
Probabilità fault: 100 %
Numero di occorrenze fault: 1
done...
kernel logs test 39
Figura 4.7 – Guasto responsabile del crash di Figura 4.6
Una volta rinvenuta l'informazione temporale è possibile trovare la corrispondenza
con il guasto responsabile esplorando il file test_xen.txt nel quale ogni test reca
nella sua descrizione il timestamp.
Come già accennato, si tratta di uno scenario in cui non è disponibile alcun log
prodotto dal kernel, proprio perché il crash del sistema impedisce al modulo
netconsole di trasferire tali log al Dom0.
In Figura 4.7 è riportata la descrizione del guasto che ha causato il crash del
dominio (Figura 4.6). In questo caso, il crash del sistema era atteso dato che il
guasto altera un bit di un puntatore che, da questo momento in poi, si riferisce ad
un'area di memoria eventualmente non accessibile. Si noti, che l'applicazione della
stessa tipologia di guasto a un bit meno significativo (ad esempio il primo),
tipicamente, non sfocia nella conseguenza appena descritta; è stato verificato,
infatti, in presenza di questo tipo di guasto, che il sistema continua la propria
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
100
esecuzione senza comportamenti anomali. È probabile, tuttavia, che tali anomalie
possano interessare il livello applicativo.
4.4.1.1.2 Restart
In questo scenario, il dominio, a seguito dell'applicazione del guasto, necessita di
un riavvio forzato. Questa condizione è rilevata dallo script di iniezione, xen_test.sh,
che si occupa di registrarla nei file log preposti (xen_test.txt). In particolare, lo script
xen_test.sh, prima di applicare un dato fault, valuta le condizioni della macchina; in
tal proposito, affinché si possa proseguire con i test senza riavviare la macchina
target devono essere verificate due condizioni: per prima cosa si verifica se è
possibile “raggiungere” il dominio tramite “ping”, in caso di esito positivo il secondo
test di raggiungibilità prevede l'accesso alla macchina tramite connessione ssh. Se
l'esito di uno dei due test proposti non dovesse rivelarsi positivo, il tool predispone il
riavvio della macchina notificando il tutto nei log.
Nel caso di irraggiungibilità, si procede col riavvio della macchina. Terminata la
campagna di iniezione, uno script, parse_log.sh, consente di individuare i casi di
test che hanno provocato un restart analizzando i log del kernel. In Figura 4.8 è
riportato quanto raccolto dal tool nel caso di condizione di “restart”. Oltre alla
descrizione del tipo di guasto, si riportano successivamente i log generati dal kernel
offrendo all'utente la possibilità di venire a conoscenza delle motivazioni che hanno
portato a tale condizione.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
101
-------------------------------------------
Target domain active...
applicazione test nr. 43
data dom 17 feb 2013, 20.40.44, CET (17_02_2013_20_40_44)
Iniezione HYPERVISOR_multicall
Iniezione sul parametro 2
Tipo iniezione: random
Probabilità fault: 100 %
Numero di occorrenze fault: 4
done...
kernel logs test 43
[ 191.177829] ------------[ cut here ]------------
[ 191.177880] kernel BUG at arch/x86/xen/multicalls.c:94!
[ 191.177917] invalid opcode: 0000 [#4] SMP
[ 191.178026] Modules linked in: netconsole
[ 191.178118]
[ 191.178141] Pid: 1042, comm: inject.sh Tainted: G D 3.2.0 #19
[ 191.178243] EIP: 0061:[<c1004947>] EFLAGS: 00010082 CPU: 0
[ 191.178282] EIP is at xen_mc_flush+0x1a7/0x1c0
[ 191.178321] EAX: fffffff2 EBX: ec7ef100 ECX: 4b6249f0 EDX: 00000000
[ 191.178348] ESI: ec7ef10c EDI: ec804000 EBP: c2329e88 ESP: c2329e48
[ 191.178394] DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0069
[ 191.178429] Process inject.sh (pid: 1042, ti=c2328000 task=c21a8ca0 task.ti=c2328000)
[ 191.178474] Stack:
[ 191.178406] c2329e94 c1007974 eb7de000 c1008780 c2037000 000001ff ec804000 00000000
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
102
[ 191.178773] 00000000 00000000 c2037000 ec804000 c2329e94 c1006c44 eb7de000 c2329eac
[ 191.179043] c1008929 c0000000 c2037000 c2037034 00000000 c2329eb4 c1008940 c2329ec4
[ 191.179310] Call Trace:
[ 191.179349] [<c1007974>] ? __xen_pgd_walk+0x144/0x170
[ 191.179411] [<c1008780>] ? xen_do_pin+0x30/0x30
[ 191.179462] [<c1006c44>] xen_mc_issue+0x34/0x40
[ 191.179413] [<c1008929>] __xen_pgd_unpin+0x79/0x90
[ 191.179464] [<c1008940>] xen_pgd_unpin+0x10/0x20
[ 191.179614] [<c1008994>] xen_exit_mmap+0x34/0x40
[ 191.179666] [<c113a10d>] arch_exit_mmap+0xd/0x10
[ 191.179717] [<c113c34f>] exit_mmap+0x2f/0xd0
[ 191.179768] [<c1010e7a>] ? spin_time_accum_total+0x2a/0x40
[ 191.179820] [<c1010f1b>] ? __xen_spin_lock+0x7b/0xb0
[ 191.179871] [<c11b34d3>] ? exit_aio+0x63/0xa0
[ 191.179924] [<c104bd64>] ? test_ti_thread_flag+0x14/0x20
[ 191.179974] [<c104bde7>] ? need_resched+0x17/0x30
[ 191.180024] [<c1062a9f>] mmput+0x4f/0xb0
[ 191.180068] [<c10690be>] exit_mm+0xde/0xf0
[ 191.180068] [<c10691c4>] do_exit+0xf4/0x300
[ 191.180068] [<c116d1dc>] ? vfs_read+0xac/0xe0
[ 191.180068] [<c116c640>] ? do_sync_write+0xe0/0xe0
[ 191.180068] [<c1069402>] do_group_exit+0x42/0x90
[ 191.180068] [<c1069468>] sys_exit_group+0x18/0x20
[ 191.180068] [<c164e7d4>] syscall_call+0x7/0xb
[ 191.180068] Code: 4b 18 8b 43 14 8b 43 0c 89 74 24 08 8b 73 20 89 74 24 04 8b 73 1c 89 34 24
e8 46 fb ff ff 89 c7 89 43 10 c1 ef 1f e9 9b fe ff ff <0f> 0b ba 81 00 00 00 b8 b9 f0 83 c1 e8 28 ec 04
00 e9 d1 fe ff
[ 191.180068] EIP: [<c1004947>] xen_mc_flush+0x1a7/0x1c0 SS:ESP 0069:c2329e48
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
103
[ 191.180068] ---[ end trace 74842dbe10b010c2 ]---
[ 191.180068] Fixing recursive fault but reboot is needed!
[ 191.180068] FAULT_INJECTION: forcing a failure
[ 191.180068] Pid: 1042, comm: inject.sh Tainted: G D 3.2.0 #19
[ 191.180068] Call Trace:
[ 191.180068] [<c1319d0d>] ? fail_dump+0x2d/0x40
[ 191.180068] [<c1319fbb>] ? should_fail+0xbb/0xf0
[ 191.180068] [<c1004647>] ? HYPERVISOR_multicall+0x197/0x2e0
[ 191.180068] [<c100aa64>] ? get_phys_to_machine+0x34/0x80
[ 191.180068] [<c100484d>] ? xen_mc_flush+0xbd/0x1c0
[ 191.180068] [<c10093b8>] ? xen_force_evtchn_callback+0x8/0x10
[ 191.180068] [<c1009e10>] ? check_events+0x8/0xc
[ 191.180068] [<c1009e07>] ? xen_restore_fl_direct_reloc+0x4/0x4
[ 191.180068] [<c10036e9>] ? arch_local_irq_restore+0x9/0x10
[ 191.180068] [<c1004440>] ? xen_end_context_switch+0x10/0x20
[ 191.180068] [<c10112ad>] ? arch_end_context_switch+0xd/0x10
[ 191.180068] [<c10119dd>] ? __switch_to+0x9d/0x110
[ 191.180068] [<c104327d>] ? context_switch.isra.109+0x8d/0xf0
[ 191.180068] [<c104a3b7>] ? pick_next_task_idle+0x17/0x20
[ 191.180068] [<c164cf47>] ? __schedule+0x97/0x1a0
[ 191.180068] [<c164d208>] ? schedule+0x18/0x20
[ 191.180068] [<c106bded>] ? run_ksoftirqd+0xfd/0x140
[ 191.180068] [<c1086b94>] ? kthread+0x64/0x70
[ 191.180068] [<c106bcf0>] ? __do_softirq+0x120/0x120
[ 191.180068] [<c1086b30>] ? create_kthread+0x40/0x40
[ 191.180068] [<c1644e7e>] ? kernel_thread_helper+0x6/0x10
[ 191.180068] ------------[ cut here ]------------
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
104
[ 191.180068] kernel BUG at arch/x86/xen/multicalls.c:94!
[ 191.180068] invalid opcode: 0000 [#4] SMP
[ 191.180068] Modules linked in: netconsole
[ 191.180068]
[ 191.180068] Pid: 1042, comm: inject.sh Tainted: G D 3.2.0 #19
[ 191.180068] EIP: 0061:[<c1004947>] EFLAGS: 00010082 CPU: 0
[ 191.180068] EIP is at xen_mc_flush+0x1a7/0x1c0
[ 191.180068] EAX: fffffff2 EBX: ec7ef100 ECX: 1e46987e EDX: 00000000
[ 191.180068] ESI: ec7ef10c EDI: c21a8ca0 EBP: ebcbff08 ESP: ebcbfed8
[ 191.180068] DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0069
[ 191.180068] Process inject.sh (pid: 1042, ti=ebcbe000 task=c21a8ca0 task.ti=c2328000)
[ 191.180068] Stack:
[ 191.180068] c10093b8 ebcbfef8 c1009e10 00000002 00000000 00000002 c1009e07 c10036e9
[ 191.180068] ebcb0040 ebcb1940 ec7f3f00 c21a8ca0 ebcbff14 c1004440 ebcb1940 ebcbff1c
[ 191.180068] c10112ad ebcbff3c c10119dd 00cb1940 00000000 ebcb1c40 16c79921 00000000
[ 191.180068] Call Trace:
[ 191.180068] [<c10093b8>] ? xen_force_evtchn_callback+0x8/0x10
[ 191.180068] [<c1009e10>] ? check_events+0x8/0xc
[ 191.180068] [<c1009e07>] ? xen_restore_fl_direct_reloc+0x4/0x4
[ 191.180068] [<c10036e9>] ? arch_local_irq_restore+0x9/0x10
[ 191.180068] [<c1004440>] xen_end_context_switch+0x10/0x20
[ 191.180068] [<c10112ad>] arch_end_context_switch+0xd/0x10
[ 191.180068] [<c10119dd>] __switch_to+0x9d/0x110
[ 191.180068] [<c104327d>] context_switch.isra.109+0x8d/0xf0
[ 191.180068] [<c104a3b7>] ? pick_next_task_idle+0x17/0x20
[ 191.180068] [<c164cf47>] ? __schedule+0x97/0x1a0
[ 191.180068] [<c164d208>] ? schedule+0x18/0x20
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
105
[ 191.180068] [<c106bded>] ? run_ksoftirqd+0xfd/0x140
[ 191.180068] [<c1086b94>] ? kthread+0x64/0x70
[ 191.180068] [<c106bcf0>] ? __do_softirq+0x120/0x120
[ 191.180068] [<c1086b30>] ? create_kthread+0x40/0x40
[ 191.180068] [<c1644e7e>] ? kernel_thread_helper+0x6/0x10
[ 191.180068] Code: 4b 18 8b 43 14 8b 43 0c 89 74 24 08 8b 73 20 89 74 24 04 8b 73 1c 89 34 24
e8 46 fb ff ff 89 c7 89 43 10 c1 ef 1f e9 9b fe ff ff <0f> 0b ba 81 00 00 00 b8 b9 f0 83 c1 e8 28 ec 04
00 e9 d1 fe ff
[ 191.180068] EIP: [<c1004947>] xen_mc_flush+0x1a7/0x1c0 SS:ESP 0069:ebcbfed8
[ 191.180068] ---[ end trace 74842dbe10b010c3 ]---
[ 191.180068] Fixing recursive fault but reboot is needed!
-------------------------------------------
Host unreachable...need to restart
Figura 4.8 – Restart del dominio
4.4.1.1.3 Hang
In questo scenario la macchina target è ancora attiva ma non risulta raggiungibile
dalla connessione ssh. Questa condizione è rilevabile facilmente dal momento che
lo script di iniezione non riesce a continuare con le operazioni successive a causa
del crash del server ssh sulla macchina target.
Si tratta di un caso speciale di “restart”, separato in base alle sue caratteristiche. In
questo caso, un rivelatore di “soft lockup” ha notato che la CPU non è più
utilizzabile da alcun processo.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
106
[ 60.008090] BUG: soft lockup - CPU#0 stuck for 22s!
[rs:main Q:Reg:326]
Figura 4.9 – Messaggio di soft lockup esibito dal kernel in caso di hang
4.4.1.1.4 Workload Failure
Questo scenario è meno critico di quelli discussi prima. Infatti, il sistema operativo
ha continuato la propria esecuzione senza problemi. Tuttavia si riscontrano delle
anomalie a livello applicativo. È possibile fare una distinzione di tali anomalie
suddividendole in quattro categorie:
- Errori interni di Apache: tali situazioni sono riportate nel file error.log di
Apache. Nei test condotti, non è stata rinvenuta alcuna anomalia di questo tipo;
- Fallimenti http: si tratta di casi in cui una richiesta http non è andata in porto a
causa del guasto iniettato; tali informazioni sono ottenibili attraverso l'analisi
congiunta dei file httperf.txt e access.log; in particolare, al termine della
sequenza di richieste http generate da httperf, è prodotto un report in cui è
possibile conoscere:
o la media delle richieste andate in porto;
o la media dei tempi di risposta.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
107
Queste informazioni sono sufficienti a provare casi di anomalie a livello
applicativo (ad esempio 404/Not Found).
La raccolta di tali informazioni avviene attraverso uno script che verifca se nel
file httperf.txt relativo al test i-esimo esistono richieste non terminate con
successo (2xx). L’output prodotto dallo script consiste nella lista di test che
hanno provocato almeno un fallimento tra le richieste http previste durante una
singola iniezione.
- Timeout http: in questo caso l'occorrenza di uno o più guasti ha provocato
l'aumento dei tempi risposta delle singole richieste http. Al termine dell’iniezione
risulta che il numero di richieste http servite è minore rispetto a quelle portate a
termine in un esecuzione tipica; anche in questo caso, dette informazioni, sono
deducibili dai file httperf.txt e access.log;
- Errori di connessione: si tratta di uno scenario in cui a seguito dell'applicazione
del guasto l'host risulta irraggiungibile e quindi non è possibile servire alcuna
richiesta http.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
108
4.4.1.3 Analisi dei risultati
L'applicazione dei test ha prodotto i risultati riassunti nelle seguenti tabelle:
Tipo fallimento # occorrenze %
Crash 10 2,4
Restart 102 24,4
Hang 13 3,24
Workload failure 64 16,0
Tabella 4.1 – Risultati 1
Dove la voce workload failure contempla i fallimenti di Apache nelle situazioni in cui
non è si è verificato alcun fallimento del sistema operativo (crash, restart, hang).
Questo risultato è stato ottenuto attraverso altri script di parsing.
Per quanto riguarda i fallimenti totali di Apache la seguente tabella riassume i
risultati ottenuti.
Tipo fallimento # occorrenze %
Errori interni di Apache 0 0
Fallimenti http 80 20
Omissioni http 154 38,5
Errori di connessione 141 35,25
Tabella 4.2 – Failure a livello applicativo
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
109
Anche in questo caso, “occorrenze” indica il numero dei test che hanno provocato
almeno uno fallimento di quelli riportati in Tabella 4.2. Inoltre, tali valori non sono da
considerarsi esclusivi. Infatti, una condizione di restart del sistema comporta:
1. Fallimenti di richieste http: le ultime richieste giunte prima del restart possono
non essere servite correttamente;
2. Omissioni di richieste: le ultime richieste giunte prima del restart possono
non essere affatto servite;
o ancora, una condizione di hang, ha il seguente effetto collaterale:
3. Richieste generate non raggiungeranno mai il server.
Figura 4.10 – Grafico failure provocati dal test
0
5
10
15
20
25
30
Crash Hang Restart Workload Failure
Failure
% Failure
10 13
102
64
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
110
Sulla base dei risultati rivenuti si può concludere che Xen presenta un buon grado
di robustezza dato che in nessuno caso l'hypervisor è andato in crash. Anche per le
macchine virtuali non sono state individuate situazioni anomale, infatti il fault rimane
circoscritto alla macchina in cui si è manifestato.
4.4.2 Risultati esperimento 2
L'esperimento due propone di valutare il grado di isolamento temporale e spaziale
messo in atto da Xen.
Figura 4.11 - Scenario di test numero due
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
111
4.4.2.1 Panoramica
La Figura 4.11 ha il solo scopo di rendere l’dea sullo scenario previsto
dall’esperimento, dato che l’approccio utilizzato è, per molti aspetti, simile
all’esperimento uno, ampiamente discusso nei paragrafi precedenti. In questo caso
le macchine virtuali previste sono due, nella fattispecie si tratta ancora di macchine
con sistema operativo Ubuntu 12.04 (kernel 3.2.0). Come accennato
nell’introduzione, lo scopo di questa campagna di test è valutare il grado di
isolamento offerto da Xen. In particolare si vuole sottoporre la macchina ”A” agli
stessi test previsti nella campagna precedente valutando come le conseguenze dei
test possano incidere sull’altra macchina, “B”, sulla quale è in esecuzione lo stesso
workload previsto per “A”. Anche in questo caso il workload consiste nel sottoporre i
due domini a sequenze di richieste http.
Poiché si è interessati al comportamento della macchina “B”, è stato necessario
modificare lo script di iniezione xen_test.sh in modo da:
- Eseguire il workload previsto (che è lo stesso di A);
- Raccogliere i log prodotti dal kernel e da Apache.
4.4.2.2 Analisi puntuale
Al termine della campagna di sperimentazione l’analisi dei risultati consiste,
principalmente, nello studio dei log prodotti dal kernel della macchina B e del file
httperf.txt generato dal singolo test. Entrambi i tipi di informazione sono stati raccolti
nelle medesime modalità previste dal test numero uno.
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
112
Come auspicato, analizzando i log del kernel non è stata rinvenuta alcuna
informazione anomala; questo conferma, dal punto di vista dell’affidabilità, quanto si
riporta più volte in letteratura riguardo la virtualizzazione che garantisce un
isolamento totale tra le varie macchine, rivelandosi come una soluzione sicura e
affidabile rispetto ad approccio non virtualizzato.
Per quel che riguarda i file httperf.txt, il loro studio è finalizzato alla valutazione delle
prestazioni. Al termine di ciascuna sequenza di richieste http generate (si ricorda
che sono previste 50 richieste per volta), il modulo httperf fornisce delle statistiche
sia prestazionali sia inerenti l’esito della sequenza di richieste. In questo caso
l’attenzione è puntata sul valore medio del tempo di risposta prodotto al termine di
ciascuna sequenza di richieste. Tale parametro è confrontato con quello ottenibile
in uno scenario di esecuzione privo di guasti.
In particolare:
1. Nella prima fase della presente sperimentazione entrambi i domini sono
sottoposti a sequenze di richieste http per un certo quanto temporale; al termine,
si avranno in output, i valori medi dei tempi di risposta di ciascuna sequenza di
richieste per ciascun dominio;
2. Si procede allo stesso modo previsto nel punto 1, con la differenza che uno dei
domini (A) sarà sottoposto anche all’iniezione dei guasti discussa all’inizio del
paragrafo.
Terminate le due fasi, con l’ausilio di alcuni script di parsing, applicati ai file
httperf.txt generati, si avrà in output:
1. La lista dei tempi medi di risposta della macchina A in assenza di test;
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
113
2. La lista dei tempi medi di risposta della macchina B in assenza di test;
3. La lista dei tempi medi di risposta della macchina A su cui sono applicati i
test;
4. La lista dei tempi medi di risposta della macchina B avendo applicato i guasti
su A.
4.4.2.3 Analisi delle prestazioni
Nel grafico di figura 4.12 è proposto un confronto tra i tempi medi di risposta della
macchina “A” di figura 4.11. Nella fattispecie:
- La curva rossa indica l’andamento della media dei tempi di risposta della
macchina sottoposta ai test;
- La curva blu indica, invece, l’andamento della media dei tempi di riposta della
stessa macchina senza l’applicazione dei test.
Come auspicabile è evidente che l’iniezione dei guasti comporta un degradamento
delle prestazioni rispetto ad un caso di riferimento (curva blu).
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
114
Figura 4.12 – Grafico tempi medi di riposta macchina “A”
Nel grafico di figura 4.13 è proposto un confronto tra i tempi medi di risposta della
macchina “B” di figura 4.11. Come accennato in precedenza in nessuno dei due
casi la suddetta macchina è stata oggetto dei test; in questa sede si vuole valutare il
suo comportamento quando vengono iniettati i guasti ad un’altra macchina presente
nell’infrastruttura (A).
Nella fattispecie:
- La curva rossa indica l’andamento della media dei tempi di risposta della
macchina applicando i test su “A”
- La curva blu indica, invece, l’andamento della media dei tempi di riposta della
stessa macchina senza l’applicazione dei test.
0
20
40
60
80
100
120
60
0
70
0
80
0
90
0
10
50
11
50
12
50
13
50
14
50
15
50
16
50
17
50
18
50
19
50
% e
spe
rim
en
ti
VM Senza fault
VM con fault
Avg reply
rate (ms)
Un’infrastruttura di robustness testing per sistemi virtualizzati in ambiente Xen
115
Figura 4.13 – Grafico tempi medi di riposta macchina “B”
Come si evince dal grafico, applicando i test alla macchina A, l’andamento dei tempi
medi di risposta si distribuisce in maniera diversa dalla distribuzione di riferimento
(curva rossa). È stato calcolato che la media dei tempi di risposta aumenta di circa
dell’11% rispetto ad un caso di assenza di guasti, per cui, è possibile concludere
che un malfunzionamento di una macchina virtuale può inficiare, anche
significativamente, sulle prestazioni delle altre macchine esistenti.
AVG reply
rate (ms)
116
Bibliografia
[1] D. Lardner. 1834 “Babbage's calculating engine”
[2] C. Babbage. 1837 “On the mathematical powers of the calculating engine”
[3] “Proceedings of the Joint AIEE-IRE Computer Conference”. 1951
[4] “Information processing systems - reliability and requirements”. 1953
[5] “Diagnostic programs and marginal checking for large scale digital computers”.
1953
[6] J. von Neumann. 1956 “Probabilistic logics and the synthesis of reliable
organisms from unreliable components”
[7] E.F. Moore and C.E. Shannon. 1965 “Reliable circuits using less reliable relays”
[8] Algirdas Avizienis, Jean-Claude Laprie, Brian Randell. Fundamental Concepts of
Dependability.
[9] J. Durães and H. Madeira. 2006 “Emulation of Software faults: A Field Data
Study and a Practical Approach”
[10] Kao, W., Iyer, R. 1994 “DEFINE: A Distributed Fault Injection and Monitoring
Environment”
117
[11] Philip Koopman, John DeVale. The Exception Handling Effectiveness of POSIX
Operating Systems
[12] Stefan Winter. 2009 Choosing Error Models for OS Robustness Evaluations
[13] J.K. Chaar et al. R. Chillarege, I.S. Bhandari. 1992 “Orthogonal defect
classification-a concept for in-process measurements”