Mondo IT

recensioni, articoli e approfondimenti sul mondo IT

Archive for the ‘xen’ Category

[EN] dig a hole in QEMU: how to execute code on the host from VMs (Part 1)

with 2 comments

In the next posts I’ll show you my recent works on QEMU. The purpose of my work is execute code on the Host machine every time that a virtual machine write or read to certain memory address. As you may know, QEMU is not largely documented, so in these posts I try to simplify works for people who are interested in these kind of operation.

The design of the solution that I’ve implemented starts from a QEMU emulated device, called LittleP. In addiction, we need of a Guest drivers that is programmed to write/read data to a fixed memory address. In my case I have used a GNU/Linux virtual machine, but probably it is possible to do the same thing with others operating systems.

So let’s start with the addiction of a new QEMU emulated device. To do this we have to add a new file (in this example littlep.c) in qemu/hw directory. This file should be something like this:

Leggi il seguito di questo post »

Written by neonum6

13/11/2012 at 12:37

XEN High Availability

with one comment

Recentemente mi sono state fatte alcune domande sulla implementazione di un piccolo cluster XEN HA. Per rispondere a quelle, e ad alcuni dubbi che possono venire quando si parla di cluster di questo tipo ho stilato una piccola guida introduttiva.

Un cluster HA XEN utilizza una SAN per condividere il disco della macchina virtuale e (almeno) due macchine XEN su cui poi si installerà il software per la realizzazione del cluster. Il passaggio di una macchina virtuale da una parte all’altra in caso di failure è effettuato attraverso migrazione. Le configurazioni delle macchine host sono del tutto indipendenti, per implementare un cluster HA non è infatti necessario avere hardware identico. Prima di fare una migrazione avviene un processo chiamato reservation: dom0 verifica sull’altro sistema se c’è spazio per allocare la macchina virtuale che vuole migrare. Questo consente banalmente di essere certi che la macchina virtuale migrata potrà eseguire sul nuovo host.

Volendo invece quantificare le risorse hardware necessarie (oltre alla SAN) è indispensabile un minimo di 2 macchine su cui è installato XEN, anche se è comunque consigliabile utilizzarne 3. Per quanto riguarda le caratteristiche di queste macchine, ipotizzando il caso più semplice con due sistemi host, bisogna calcolare tutto in modo che se una cade l’altra abbia le capacità (CPU e RAM, ecc) per gestire il carico.
Migrare macchine paravirtualizzate è un processo ottimizzato e abbastanza stabile. La migrazione di macchine HVM invece è possibile, ma c’è da considerare che, a differenza delle soluzioni paravirtualizzate, la virtual machine non è conscia della migrazione. Dunque potrebbero esserci dei problemi che dipendono dal sistema operativo installato nella vm. Per questo e per motivi di velocità è meglio migrare macchine paravirtualizzate. Infatti nell’ultima versione (XEN 4) ci sono alcun bug aperti relativi proprio alla migrazione HVM.

Written by neonum6

04/08/2011 at 13:02

Uno scheduler per XEN

leave a comment »

questo è uno scheduler (random) per XEN. Il suo valore è semplicemente accademico senza alcuna applicazione pratica.

This is a random scheduler for XEN. I wrote this scheduler for academic purposes only.

/****************************************************************************
 * (C) 2010 - Michele Paolino
 ****************************************************************************
 *
 *        File: common/sched_random.c
 *      Author: Michele Paolino
 *
 *      Description: Random CPU scheduler
 */

#include <xen/lib.h>
#include <xen/sched.h>
#include <xen/time.h>
#include <xen/sched-if.h>
#include <xen/softirq.h>
#include <xen/errno.h>
#include <xen/list.h>
#include <xen/timer.h>
#include <xen/randmd5.h>

int tot_vcpu=0;
MD5_CTX mdContext;

#define RAND_PCPU(_c)     ((struct random_pcpu *)per_cpu(schedule_data, _c).sched_priv)
#define RAND_VCPU(_vcpu)  ((struct random_vcpu *) (_vcpu)->sched_priv)
#define RUNQ(_cpu)        (&(RAND_PCPU(_cpu)->runq))

struct random_pcpu {
 struct list_head runq;
};

struct random_vcpu{
 struct list_head runq_elem;
 struct vcpu *vcpu;
};

static inline void __runq_tickle(unsigned int cpu, struct random_vcpu *new)
{
 struct random_vcpu * const cur = RAND_VCPU(per_cpu(schedule_data, cpu).curr);
 cpumask_t mask;

 ASSERT(cur);
 cpus_clear(mask);
 printk("\nTickle vcpu %d\n", cur->vcpu->vcpu_id);

 if ( vcpu_runnable(cur->vcpu) )
 {
 cpu_set(cpu, mask);
 printk("\nI'm setting the mask for the current vcpu\n");
 }
 /* Send scheduler interrupts to designated CPUs */
 if ( !cpus_empty(mask) ){
 printk("\nRaising softirq\n");
 cpumask_raise_softirq(mask, SCHEDULE_SOFTIRQ);
 }
}

static inline int  __vcpu_on_runq(struct random_vcpu *svc)
{
 return !list_empty(&svc->runq_elem);
}

static inline void __runq_insert(unsigned int cpu, struct random_vcpu *svc)
{
 struct list_head *runq = RUNQ(cpu);
 BUG_ON( cpu != svc->vcpu->processor );
 if (!__vcpu_on_runq(svc)) list_add(&svc->runq_elem,runq);
 return;
}

static inline void __runq_remove(struct random_vcpu *svc)
{
 BUG_ON( !__vcpu_on_runq(svc) );
 list_del_init(&svc->runq_elem);
}

static inline void __random_vcpu_check(struct vcpu *vc)
{
 struct random_vcpu * const svc = RAND_VCPU(vc);
 BUG_ON( (svc->vcpu) != vc );
}

static int random_pcpu_init(int cpu)
{
 struct random_pcpu *spc;

 /* Allocate per-PCPU info */

 spc = xmalloc(struct random_pcpu);
 if ( spc == NULL )
 return -1;
 memset(spc, 0, sizeof(*spc));

 INIT_LIST_HEAD(&spc->runq);
 per_cpu(schedule_data, cpu).sched_priv = spc;
 return 0;
}

static int random_vcpu_init(struct vcpu *vc){
 struct random_vcpu *svc;

 svc = xmalloc(struct random_vcpu);
 if ( svc == NULL )
 return -1;
 memset(svc, 0, sizeof(*svc));

 INIT_LIST_HEAD(&svc->runq_elem);

 svc->vcpu = vc;
 vc->sched_priv = svc;

 /* Allocate per-PCPU info */
 if ( unlikely(!RAND_PCPU(vc->processor)) )
 {
 if ( random_pcpu_init(vc->processor) != 0 )
 return -1;
 }
 __random_vcpu_check(vc);

 tot_vcpu++;
 printk("\nadd VCPU %d tot_vcpu=%d\n", vc->vcpu_id, tot_vcpu);
 return 0;
}

static void random_vcpu_destroy(struct vcpu *vc)
{
 struct random_vcpu * const svc = RAND_VCPU(vc);
 printk("\n\nDestroying vcpu %d\n\n",vc->vcpu_id);
 tot_vcpu--;
 BUG_ON( !list_empty(&svc->runq_elem) );
 list_del(&svc->runq_elem);
 xfree(svc);
}

struct task_slice random_schedule(s_time_t now){
 const unsigned int cpu = smp_processor_id();
 struct list_head * runq = RUNQ(cpu);
 struct random_vcpu * const scurr = RAND_VCPU(current);
 struct random_vcpu *snext;
 struct task_slice ret;
 int next_vcpu=0;

 next_vcpu = (MD5Final (&mdContext)) % tot_vcpu;
 __random_vcpu_check(current);

 if ( vcpu_runnable(current) )
 __runq_insert(cpu, scurr);
 else
 BUG_ON( is_idle_vcpu(current) || list_empty(runq) );

 while (next_vcpu!=0){
 next_vcpu--;
 list_move(runq, runq->next);
 }

 snext = list_entry(runq->next, struct random_vcpu, runq_elem);
 __runq_remove(snext);

 ret.time = MILLISECS(10);
 ret.task=snext->vcpu;    
 __random_vcpu_check(current);
 return ret;
}

static void __random_init(void){
 unsigned char digest[16];
 int i;
 long unsigned int mask= 0xFF;
 long unsigned int string_now = NOW();
 MD5Init (&mdContext);
 for (i=0;i<8;i++){
 digest[i]=(char) ((string_now & mask) >> 8*i);
 mask <<=8;
 }
 MD5Update (&mdContext, (unsigned char*)digest, sizeof(s_time_t));
}

static void random_vcpu_wake( struct vcpu *vc){
 struct random_vcpu * const svc = RAND_VCPU(vc);
 const unsigned int cpu = vc->processor;
 BUG_ON ( is_idle_vcpu(vc) );

 /* Put the VCPU on the runq and tickle CPUs */
 __runq_insert(cpu, svc); //inserisco in coda
 __runq_tickle(cpu, svc);  // raise softirq   

 return;
}

static void random_vcpu_sleep(struct vcpu *vc)
{
 struct random_vcpu * const svc = RAND_VCPU(vc);
 printk("Sleeping..\n");
 BUG_ON( is_idle_vcpu(vc) );
 if ( per_cpu(schedule_data, vc->processor).curr == vc )
 cpu_raise_softirq(vc->processor, SCHEDULE_SOFTIRQ);
 else if ( __vcpu_on_runq(svc) )
 __runq_remove(svc);
}

static void random_init(void)
{
 tot_vcpu=0;
 __random_init();
}

struct scheduler sched_rand_def = {
 .name           = "Random Scheduler",
 .opt_name       = "random",
 .sched_id       = 17,
 .init_vcpu      = random_vcpu_init,
 .wake           = random_vcpu_wake,
 .sleep          = random_vcpu_sleep,
 .destroy_vcpu   = random_vcpu_destroy,
 .do_schedule    = random_schedule,
 .init           = random_init,
};

Written by neonum6

21/05/2010 at 22:16

redirigere su file console seriale di QEMU/KVM

leave a comment »

una volta configurata la macchina virtuale kvm/qemu a stampare tutto l’output su porta seriale è possibile passando come paramentro -serial a qemu/kvm redirigere questo flusso su file. Ipotizzando che il file si chiama logseriale.txt

kvm -m 1G -hda xen.qcow2 -serial file:logseriale.txt

Written by neonum6

28/04/2010 at 15:16

Articolo tecnico su XEN

leave a comment »

Segnalo la pubblicazione di un articolo tecnico riguardante l’architettura del sistema di virtualizzazione XEN.
per leggere l’articolo completo cliccate qui.

In questo periodo sto pubblicando buona parte del materiale che ne è venuto fuori dal mio studio su XEN. Se siete interessati all’argomento fate ogni tanto un giro da queste parti perché presto ci saranno nuovi articoli.

Written by neonum6

02/04/2010 at 12:19

Pubblicato su articoli, xen

Tagged with ,

XEN: Gli scheduler SEDF e Credit

with 2 comments

Come funzionano gli scheduler tuttora utilizzati dal sistema di virtualizzazione XEN? Lo spiegheremo in questo articolo.

Sono due gli scheduler presenti e utilizzabili in questo momento nell’hypervisor di XEN, entrambi in grado di lavorare in work conserving mode. La work conserving mode è la capacità di uno scheduler di dare a un dominio, in alcune condizioni, l’intera CPU.

Credit scheduler è quello utilizzato di default dall’hypervisor, è il più giovane e ancora in fase di sviluppo. SEDF invece non è più sviluppato ma viene ancora rilasciato perché più stabile per alcune specifiche applicazioni. A livello algoritmico i due scheduler differiscono per i criteri con cui ordinano una coda di VCPU eseguibili. Queste code sono pari alla quantità di processori fisici presenti nel sistema. A ogni dominio può essere assegnata più di una CPU virtuale.

SEDF scheduler

Simple Earliest Deadline First (SEDF) è molto semplice e basa il suo funzionamento su tre variabili, p (period), s(slice) e x(extra time). L’algoritmo prevede che una macchina virtuale deve avere accesso alla CPU per s millisecondi ogni p millisecondi. Il valore x invece è un flag che determina se al dominio è permesso tempo-CPU extra utile per abilitare il work conserving mode. Questi valori sono configurabili dominio per dominio.

SEDF mantiene per ogni VCPU il tempo rimasto prima della scadenza del periodo (deadline), e la somma dei tempi processore che ancora spettano al dominio prima dello scadere del periodo. I processori virtuali sono ordinati in una coda in base alla deadline più vicina. Quando le VCPU vengono schedulate, la loro deadline si sposta avanti nel tempo. Nel caso in cui lo slice di una macchina non permette di realizzare il rapporto p/s delle altre, esso viene ridotto.

SEDF manca del supporto al load balancing su sistemi multiprocessore e valuta le statistiche di utilizzo del processore fisico per VCPU.

Credit scheduler

Credit scheduler è usato dalle più recenti versioni di XEN come scheduler di default. Ogni dominio ha due variabili associate ad esso, un peso (weight) e un limite (cap). Il peso rappresenta la quantità di CPU disponibile per il dominio in ogni unità di tempo, il limite invece ne stabilisce il massimo. Esso è un valore assoluto che descrive la percentuale di CPU di cui può usufruire il dominio. I pesi invece sono gli uni relativi agli altri. Dare a tutte le macchine virtuali un peso uguale, di qualunque valore esso sia, comporta gli stessi effetti su tutti i domini.

Ogni CPU gestisce una lista ordinata di VCPU eseguibili. La priorità è settata su due valori: under e over. Questi valori indicano se le VCPU hanno superato o meno il tempo d’uso che spettava loro del processore fisico nel periodo corrente. Quando viene inserita una VCPU in coda viene posta dopo tutte le altre di uguale priorità.

Ogni volta che una CPU virtuale viene messa in esecuzione, consuma alcuni dei suoi crediti. Se il conto dei crediti consumati è negativo, la priorità della VCPU viene impostata a over.

Quando la somma dei crediti di tutti i domini è negativa, vengono assegnati nuovi crediti a tutti i domini. Ogni volta che la funzione di scheduling do_schedule deve decidere quale VCPU schedulare, preleva semplicemente l’elemento che è in testa alla coda.

Prima di andare in idle un processore fisico verifica che non ci siano VCPU eseguibili. Inoltre quando un processore non trova VCPU che sono under nella sua coda va a cercarla nella coda di altri eventuali processori. Questo comportamento garantisce load balancing su sistemi multiprocessore.

A differenza di SEDF, Credit valuta le statistiche di utilizzo del processore fisico per dominio.

Written by neonum6

29/03/2010 at 21:35

XEN: come viene descritto un dominio

leave a comment »

Un dominio nel sistema di virtualizzazione XEN è una entità che incapsula al suo interno un ambiente completo per l’esecuzione di un sistema operativo ospite.

Nella struttura domain sono contenute tutte le informazioni relative al dominio e alle risorse ad esso allocate. Questa viene definita nel file sched.h contenuto nella cartella xen/include/xen/. Attraverso tale struttura, quindi, ogni macchina virtuale in esecuzione viene gestita e rappresentata.

Ne verrà discussa ora la parte più significativa :

struct domain

{

domid_t domain_id;

spinlock_t page_alloc_lock; /* protects all the following fields */

struct page_list_head page_list; /* linked list, of size tot_pages */

struct page_list_head xenpage_list; /* linked list (size xenheap_pages) */

unsigned int tot_pages; /* number of pages currently possesed */

unsigned int max_pages; /* maximum value for tot_pages */

unsigned int xenheap_pages; /* # pages allocated from Xen heap */

/* Scheduling. */

void *sched_priv; /* scheduler-specific data */

struct domain *next_in_list;

struct domain *next_in_hashbucket;

struct list_head rangesets;

spinlock_t rangesets_lock;

struct vcpu *vcpu[MAX_VIRT_CPUS];

shared_info_t *shared_info; /* shared data area */

/* Event channel information. */

struct evtchn *evtchn[NR_EVTCHN_BUCKETS];

spinlock_t event_lock;

struct grant_table *grant_table;

};

Come è facile immaginare, ogni dominio è caratterizzato da un identificatore rappresentato dalla variabile domain_id. All’interno della struttura vengono anche memorizzate tutte le informazioni riguardanti le pagine di memoria dedicate alla macchina ospite. In particolare, le variabili tot_pages e max_pages descrivono il numero massimo e quello attuale di pagine di memoria fruibili dal dominio.

Per quanto riguarda lo scheduling ogni dominio conserva un puntatore a void di nome sched_priv utile agli sviluppatori dello scheduler per allocare a quell’indirizzo dati utili al corretto svolgimento delle funzioni di scheduling. L’ultima variabile presente nella sezione scheduling è un array di CPU virtuali (VCPU). Ogni CPU virtuale è una astrazione del processore fisico della macchina.

La variabile shared_info contiene informazioni aggiornate dinamicamente run-time. Essa è utilizzata dalla macchina virtuale per reperire informazioni sullo stato globale del sistema, quali ad esempio le VCPU del dominio in stato runnable, gli event channel disponibili, il tempo di sistema e alcune informazioni relative all’architettura.

Infine viene descritta la sezione riguardante gli event channel. Questi sono un meccanismo per notifiche asincrone all’interno di XEN. Assieme alle shared memory pages, gli event channel vengono utilizzati per comunicazioni tra back-end e front-end di device driver o di applicazioni come XenStore.

Il codice contenuto nelle parti non commentate consiste in flag che descrivono caratteristiche della macchina virtuale come ad esempio il fatto se una macchina è paravirtualizzata o meno.

Written by neonum6

24/03/2010 at 23:53

XEN bug

with 2 comments

Ieri mentre cercavo di compilare XEN ho trovato un bug nel makefile. Ad oggi il problema riguarda sia la versione 3.4.2 sia la versione unstable 4.0.0 rc. Il makefile va in errore ogni volta che la cartella dei sorgenti è contenuta in un path che contiene cartelle con degli spazi del nome.

Se ad esempio mettiamo i sorgenti in una cartella chiamata “working on xen” il compilatore torna:

neonum6@neonum6-laptop:~/Scrivania/working on xen/xen4.0$ make world
Config.mk:26: /home/neonum6/Scrivania/working: Nessun file o directory
Config.mk:26: on: Nessun file o directory
Config.mk:26: xen/xen4.0/config/Linux.mk: Nessun file o directory
Config.mk:27: /home/neonum6/Scrivania/working: Nessun file o directory
Config.mk:27: on: Nessun file o directory
Config.mk:27: xen/xen4.0/config/x86_64.mk: Nessun file o directory
Config.mk:26: /home/neonum6/Scrivania/working: Nessun file o directory
Config.mk:26: on: Nessun file o directory
Config.mk:26: xen/xen4.0/config/Linux.mk: Nessun file o directory
Config.mk:27: /home/neonum6/Scrivania/working: Nessun file o directory
Config.mk:27: on: Nessun file o directory
Config.mk:27: xen/xen4.0/config/x86_64.mk: Nessun file o directory
make: *** Nessuna regola per creare l’obiettivo «xen/xen4.0/config/x86_64.mk».  Arresto.

Ho segnalato il bug su bugzilla di XEN, qui.

Written by neonum6

11/02/2010 at 09:41

promemoria pre-compilazione XEN – Packages to install before compile XEN

leave a comment »

Ecco la lista dei pacchetti che è necessario installare prima di compilare XEN su un sistema Debian-Ubuntu / Packages to install to compile XEN on Ubuntu-Debian based OSes:

build-essential debootstrap libconfig-inifiles-perl libtext-template-perl rinse xfsprogs bridge-utils libvncserver libvncserver-dev  libgcrypt-dev libgcrypt11-dev gettext libx11-dev libcurl4-openssl-dev libgcrypt-dev libcrypto++-dev bin86 bcc libgnutls-dev zlib-bin zlibg1-dev gawk mercurial libncurses5 libncurses-dev zlib1g-dev pciutils-dev python-dev makeinfo texinfo

i pacchetti possono cambiare da versione a versione ma grossomodo dovrebbero essere gli stessi 😉

Written by neonum6

27/01/2010 at 11:33

Compilare kernel dom0

with 4 comments

Spesso accade che ci troviamo il kernel del dom0 un pò datato, o a causa di politiche della ditribuzione che staimo utilizzando (sicurezza, stabilità, mancato supporto come fa ubuntu :@ ). Ecco allora come fare per avere il nostro kernel sempre aggiornato. Questa guida si riferisce all’ultima versione di kernel-patch disponibili per xen.

Più in generale non è possibile installare qualsiasi kernel su dom0, bensì solo quelli per cui sono state sviluppate le patch apposite.

Innanzitutto scarichiamo quello che ci serve: il kernel linux-2.6.30.3 dal sito ufficiale, il pacchetto delle patch su googlecode. Spostiamo il file compresso in /usr/src e decomprimiamo i sorgenti del kernel con il comando sudo tar -xvf linux-2.6.30.3.tar.bz. Creo la cartella /usr/src/linux-2.6.30.3/patch. Sposto il file xen-patches-2.6.30-3.tar.bz2 in usr/src/linux-2.6.30.3/patch. Usiamo questo script per applicare le patch

#!/bin/bash
for P in `ls patch/6*.patch1 | sort `
do
patch -p1 -s -i $P
if [ $? = 0 ]; then
echo $P applied
else
echo "Error processing "$P
exit 1
fi
done

Ora ci assicuriamo che siano così attive queste voci(di default lo sono):

Processor type and features ->
[*] Symmetric multi-processing support
[ ] Support sparse irq numbering
[*] Enable MPS table
[*] Enable Xen compatible kernel
[*] Single-depth WCHAN output
Processor family (Generic-x86-64) --->
[ ] Configure Maximum number of SMP Processors and NUMA Nodes
(64) Maximum number of CPUs
Preemption Model (No Forced Preemption (Server)) --->
[ ] Machine Check Exception

Device Drivers -> XEN
[*] Privileged Guest (domain 0)
<*> Backend driver support
<*> Block-device backend driver
<*> Block-device tap backend driver
<*> Block-device tap backend driver 2
>*> Network-device backend driver
(8) Maximum simultaneous transmit requests (as a power of 2)
[ ] Pipelined transmitter (DANGEROUS)
< > Network-device loopback driver
<*> PCI-device backend driver
PCI Backend Mode (Virtual PCI) —>
[ ] PCI Backend Debugging
< > TPM-device backend driver
<M> SCSI backend driver
<M> USB backend driver
<M> Block-device frontend driver
<M> Network-device frontend driver
<M> Network-device frontend driver acceleration for Solarflare NICs
<M> SCSI frontend driver
<M> USB frontend driver
[*] Taking the HCD statistics (for debug)
<*> User-space granted page access driver
<*> Framebuffer-device frontend driver

Abbiamo inoltre bisogno di attivare la funzionalità bridging nel kernel. Ci assicuriamo che ci siano “802.1d Ethernet Bridging” (in “Networking options”) e  “Universal TUN/TAP device driver support” (in “Network device support”).

Finalmente possiamo compilare:

make

make install

make modules_install

mkintramfs -o /boot/initrd.img-2.6.30.3 2.3.30.3

Ora apriamo il file /boot/grub/menu.lst e da root inseriamo:

title           Xen 3.3 / Ubuntu 8.04.3 LTS, kernel 2.6.30.3-xen
root            (hd0,0)
kernel          /boot/xen-3.3.gz
module          /boot/vmlinuz-2.6.30.3 root=UUID=7d8057c5-f4cd-4467-99d7-bb6f46def3c6 ro console=tty0 pciback.ide=(00:19.0)
module          /boot/initrd.img-2.6.30.3
quiet

Ovviamente c’è da adattare la versione dell’hypervisor installata sulla vostra macchina e le opzioni da dare a grub e al kernel…

Ora riavviamo e il nostro kernel nuovo fiammante è pronto per essere utilizzato.

Written by neonum6

03/09/2009 at 12:49