We currently migrate to MediaWiki from our old installation, but not all content has been migrated yet. Take a look at the Wiki Team page for instructions how to help or browse through our new wiki at wiki.linux-vserver.org to find the information already migrated.

12. Implementation au niveau Noyau

Ce chapitre est plus particulierement reserve aux developpeurs noyau. Toutefois, les curieux trouveront avantage a tirer le rideau pour decouvrir les entrailles du systeme.

12.0. Appel systeme

Pendant un certain temps, Linux-VServer a utilise different appels systeme pour accomplir les differents aspects de son travail. Tres vite, le nombre des commandes necessaires est devenu important, nous avons donc commence a donner des valeurs magiques aux appels pour choisir le comportement desire.

Enfin, il y a peu de temps, nous avons obtenu un numero reserve pour les appels systemes Linux-VServer, et malgre l'opinion de certains developpeurs, c'est globalement une bonne decision de garder un seul appel systeme.

Avoir plusieurs appels systeme peut faciliter les choses pour travailler avec les appels systemes sur plusieurs architectures; toutefois, cela n'a pas constitue un probleme majeur jusqu'ici, les donnees transitant depuis et vers le noyau etant, conformement au standard C99, fortement typees.

Quoi qu'il en soit, la mise en place d'un appel systeme passe par la creation d'un multiplexeur, qui va decider quelle commande doit etre executee, et quels arguments lui passer. A elle par la suite d'executer effectivement le travail demande.

 extern asmlinkage long
 sys_vserver(uint32_t cmd, uint32_t id, void __user *data)

L'appel systeme Linux-VServer prend trois arguments dans tous les cas: la commande (cmd), un nombre (id), et une structure de donnee en espace utilisateur d'une taille encore inconnue.

Pour faciliter le deboggage et la tenue a jour des informations de versionning, la chaine cmd est decomposee en trois parties: les 12 bits de poids faible contiennent le numero de version, puis 4 bits sont reserves. Les 16 bits de poids fort sont divises en 8 bits de commande et 6 pour la categorie, puis les 2 bits restant, reserves pour usage ulterieur.

Nous avons donc 64 Categories avec chacune 256 commandes, et 4096 revisions possible pour chaque commande, ce qui surpasse largement nos besoins presents et futurs.

Voici un apercu des categories deja definies, et leur valeur numerique associee:

 Matrice des appels systeme V2.6

        |VERSION|CREATE |MODIFY |MIGRATE|CONTROL|EXPERIM| |SPECIAL|SPECIAL|
        |STATS  |DESTROY|ALTER  |CHANGE |LIMIT  |TEST   | |       |       |
        |INFO   |SETUP  |       |MOVE   |       |       | |       |       |
 -------+-------+-------+-------+-------+-------+-------+ +-------+-------+
 SYSTEM |VERSION|VSETUP |VHOST  |       |       |       | |DEVICES|       |
 HOST   |     00|     01|     02|     03|     04|     05| |     06|     07|
 -------+-------+-------+-------+-------+-------+-------+ +-------+-------+
 CPU    |       |VPROC  |PROCALT|PROCMIG|PROCTRL|       | |SCHED. |       |
 PROCESS|     08|     09|     10|     11|     12|     13| |     14|     15|
 -------+-------+-------+-------+-------+-------+-------+ +-------+-------+
 MEMORY |       |       |       |       |       |       | |SWAP   |       |
        |     16|     17|     18|     19|     20|     21| |     22|     23|
 -------+-------+-------+-------+-------+-------+-------+ +-------+-------+
 NETWORK|       |VNET   |NETALT |NETMIG |NETCTL |       | |SERIAL |       |
        |     24|     25|     26|     27|     28|     29| |     30|     31|
 -------+-------+-------+-------+-------+-------+-------+ +-------+-------+
 DISK   |       |       |       |       |       |       | |INODE  |       |
 VFS    |     32|     33|     34|     35|     36|     37| |     38|     39|
 -------+-------+-------+-------+-------+-------+-------+ +-------+-------+
 OTHER  |       |       |       |       |       |       | |VINFO  |       |
        |     40|     41|     42|     43|     44|     45| |     46|     47|
 =======+=======+=======+=======+=======+=======+=======+ +=======+=======+
 SPECIAL|       |       |       |       |FLAGS  |       | |       |       |
        |     48|     49|     50|     51|     52|     53| |     54|     55|
 -------+-------+-------+-------+-------+-------+-------+ +-------+-------+
 SPECIAL|       |       |       |       |RLIMIT |SYSCALL| |       |COMPAT |
        |     56|     57|     58|     59|     60|TEST 61| |     62|     63|
 -------+-------+-------+-------+-------+-------+-------+ +-------+-------+

La definition de ces commandes est simplifiee grace a certaines macros, comme par exemple interroger un drapeau ou lui attribuer une valeur:

 #define VCMD_get_cflags         VC_CMD(FLAGS, 1, 0)
 #define VCMD_set_cflags         VC_CMD(FLAGS, 2, 0)

 extern int vc_get_cflags(uint32_t, void __user *);
 extern int vc_set_cflags(uint32_t, void __user *);

Notez bien, ce n'est pas la commande elle meme qui est passee en parametre, mais un id et un pointeur vers une structure de donnee en espace utilisateur.

12.1. Structures de Donnees Utilisees

Plusieurs types de structures sont utilisees par les differentes parties de l'implementation; seuls quelques exemples sont donnes ici, l'ensemble complet des structures utilisees se trouve dans le source.

12.1.0. La Structure Data Context

La structure Data Context permet de gerer les contextes, leur destruction, ainsi que les futurs contextes hierarchiques.

Les sections reservees pour l'ordonanceur ou les limites sont definies dans des structures separees, comme le veut la logique, puis incorporees dans la structure principale.

 struct vx_info {
         struct list_head vx_list;         /* linked list of contexts */
         xid_t vx_id;                      /* context id */
         atomic_t vx_refcount;             /* refcount */
         struct vx_info *vx_parent;        /* parent context */

         struct namespace *vx_namespace;   /* private namespace */
         struct fs_struct *vx_fs;          /* private namespace fs */
         uint64_t vx_flags;                /* context flags */
         uint64_t vx_bcaps;                /* bounding caps (system) */
         uint64_t vx_ccaps;                /* context caps (vserver) */

         pid_t vx_initpid;                 /* PID of fake init process */

         struct _vx_limit limit;           /* vserver limits */
         struct _vx_sched sched;           /* vserver scheduler */
         struct _vx_cvirt cvirt;           /* virtual/bias stuff */
         struct _vx_cacct cacct;           /* context accounting */

         char vx_name[65];                 /* vserver name */
 };

Voici un exemple de la sous-structure Ordonanceur:

 struct _vx_sched {
         spinlock_t tokens_lock; /* lock for this structure */

         int fill_rate;          /* Fill rate:      add X tokens ... */
         int interval;           /* Divisor:      ... each Y jiffies */
         atomic_t tokens;        /*         current number of tokens */
         int tokens_min;         /* Limit:        minimum for unhold */
         int tokens_max;         /* Limit:     no more than N tokens */
         uint32_t jiffies;       /* bias:     integral multiple of Y */

         uint64_t ticks;         /* token tick events */
         cpumask_t cpus_allowed; /* cpu mask for context */
 };

L'idee principale derriere ces sous-structures est de faciliter leur maintenabilite, a l'aide, pour chacune d'elles, d'une fonction init et cleanup, d'ou une lecture facilitee et une maintenabilite accrue.

12.1.1. The Scheduler Command Data

As an example for the data structure used to control a specific part of the context from user-space, here is a scheduler command and the utilized data structure to set the properties:

 #define VCMD_set_sched          VC_CMD(SCHED, 1, 2)

 struct  vcmd_set_sched_v2 {
         int32_t fill_rate;      /* Fill rate:      add X tokens ... */
         int32_t interval;       /* Divisor:      ... each Y jiffies */
         int32_t tokens;         /*         current number of tokens */
         int32_t tokens_min;     /* Limit:        minimum for unhold */
         int32_t tokens_max;     /* Limit:     no more than N tokens */
         uint64_t cpu_mask;      /* Mask:               allowed cpus */
 };

12.1.2. Example Accounting: Sockets

Basically all the accounting and limit stuff are defined as macros or inline functions capable of handling the different resources, hiding the underlying implementation wherever possible.

 #define vx_acc_sock(v,f,p,s) \
         __vx_acc_sock((v), (f), (p), (s), __FILE__, __LINE__)

 static inline void __vx_acc_sock(struct vx_info *vxi,
         int family, int pos, int size, char *file, int line)
 {
         if (vxi) {
                 int type = vx_sock_type(family);

                 atomic_inc(&vxi->cacct.sock[type][pos].count);
                 atomic_add(size, &vxi->cacct.sock[type][pos].total);
         }
 }

 #define vx_sock_recv(sk,s) \
         vx_acc_sock((sk)->sk_vx_info, (sk)->sk_family, 0, (s))
 #define vx_sock_send(sk,s) \
         vx_acc_sock((sk)->sk_vx_info, (sk)->sk_family, 1, (s))
 #define vx_sock_fail(sk,s) \
         vx_acc_sock((sk)->sk_vx_info, (sk)->sk_family, 2, (s))

And this general definition is then used where appropriate, for example in the __sock_sendmsg() function like this:

         len = sock->ops->sendmsg(iocb, sock, msg, size);
         if (sock->sk) {
                 if (len == size)
                         vx_sock_send(sock->sk, size);
                 else
                         vx_sock_fail(sock->sk, size);
         }

12.1.3. Example Limits: Virtual Memory

 #define vx_pages_avail(m, p, r) \
         __vx_pages_avail((m)->mm_vx_info, (r), (p), __FILE__, __LINE__)

 static inline int __vx_pages_avail(struct vx_info *vxi,
                 int res, int pages, char *file, int line)
 {
         if (!vxi)
                 return 1;
         if (vxi->limit.rlim[res] == RLIM_INFINITY)
                 return 1;
         if (atomic_read(&vxi->limit.res[res]) +
                 pages < vxi->limit.rlim[res])
                 return 1;
         return 0;
 }

 #define vx_vmpages_avail(m,p)  vx_pages_avail(m, p, RLIMIT_AS)
 #define vx_vmlocked_avail(m,p) vx_pages_avail(m, p, RLIMIT_MEMLOCK)
 #define vx_rsspages_avail(m,p) vx_pages_avail(m, p, RLIMIT_RSS)

And again the test against those limits at certain places, for example here in copy_process()

         /* check vserver memory */
         if (p->mm && !(clone_flags & CLONE_VM)) {
                 if (vx_vmpages_avail(p->mm, p->mm->total_vm))
                         vx_pages_add(p->mm->mm_vx_info,
                                 RLIMIT_AS, p->mm->total_vm);
                 else
                         goto bad_fork_free;
         }

12.1.4. Example Virtualization: Uptime

 void vx_vsi_uptime(struct timespec *uptime)
 {
         struct vx_info *vxi = current->vx_info;

         set_normalized_timespec(uptime,
                 uptime->tv_sec - vxi->cvirt.bias_tp.tv_sec,
                 uptime->tv_nsec - vxi->cvirt.bias_tp.tv_nsec);
         return;
 }
         if (vx_flags(VXF_VIRT_UPTIME, 0))
                 vx_vsi_uptime(&uptime, &idle);

[Index] [Précédent] [Suivant]