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.
Ce chapitre est plus particulierement reserve aux developpeurs noyau. Toutefois, les curieux trouveront avantage a tirer le rideau pour decouvrir les entrailles du 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 *);
Note that the command itself is not passed to the actual command implementation, only the id and the pointer to user-space data.
There are many different data structures used by different parts of the implementation; while only a few examples are given here, all utilized structures can be found in the source.
The Context Data Structure consists of a few fields required to manage the contexts, and handle context destruction, as well as future hierarchical contexts.
Logically separated sections of that structure, like for the scheduler or the context limits are defined in separate structures, and incorporated into the main one.
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 */ };
Here as example the Scheduler Substructure:
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 */ };
The main idea behind this separation is that each substructure belongs to a logically distinct part of the implementation which provides an init and cleanup function for this structure, thus simplifying maintainability and readability of those structures.
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 */ };
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); }
#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; }
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);