userfaultfd(2) System Calls Manual userfaultfd(2) NOM userfaultfd - Creer un descripteur de fichier pour gerer les erreurs de page en espace utilisateur BIBLIOTHEQUE Bibliotheque C standard (libc, -lc) SYNOPSIS #include /* Definition des constantes O_* */ #include /* Definition des constantes SYS_* */ #include /* Definition des constantes UFFD_* */ #include int syscall(SYS_userfaultfd, int flags); Note : la glibc ne fournit pas d'enveloppe pour userfaultfd(), imposant l'utilisation de syscall(2). DESCRIPTION userfaultfd() cree un nouvel objet userfaultfd qui peut etre utilise pour la delegation de la gestion des erreurs de page a une application de l'espace utilisateur et renvoie un descripteur de fichier qui fait reference au nouvel objet. Le nouvel objet userfaultfd est configure en utilisant ioctl(2). Une fois l'objet userfaultfd configure, l'application peut utiliser read(2) pour recevoir des notification d'userfaultfd. Les lectures a partir d'userfaultfd peuvent etre bloquantes ou non bloquantes en fonction de la valeur des attributs (flags) utilises pour la creation de l'userfaultfd ou des appels suivants a fcntl(2). Les valeurs suivantes peuvent etre combinees dans flags par un OU binaire pour modifier le comportement d'userfaultfd() : O_CLOEXEC Activer l'attribut close-on-exec pour le nouveau descripteur de fichier userfaultfd. Consultez la description de l'attribut O_CLOEXEC dans open(2). O_NONBLOCK Permettre une operation non bloquante pour l'objet userfaultfd. Voir la description de l'attribut O_NONBLOCK dans open(2). UFFD_USER_MODE_ONLY C'est un attribut specifique a userfaultfd qui a ete introduit dans Linux 5.11. Quand il est defini, l'objet userfaultfd ne pourra gerer que les erreurs de page provenant de l'espace utilisateur dans les regions enregistrees. Quand une erreur provenant du noyau est declenchee dans l'intervalle enregistre avec cet userfaultfd, un signal SIGBUS sera envoye. Quand le dernier descripteur de fichier faisant reference a un objet userfaultfd est ferme, tous les intervalles de memoire qui ont ete enregistres avec l'objet sont desenregistres et les evenements non lus sont vides. Userfaultfd gere trois modes d'enregistrement : UFFDIO_REGISTER_MODE_MISSING (depuis Linux 4.10) Quand il est enregistre avec le mode UFFDIO_REGISTER_MODE_MISSING, l'espace utilisateur recevra une notification d'erreur de page lors de l'acces a une page manquante. L'execution du thread fautif sera arretee jusqu'a ce que l'erreur de page soit resolue a partir de l'espace utilisateur par un ioctl UFFDIO_COPY ou UFFDIO_ZEROPAGE. UFFDIO_REGISTER_MODE_MINOR (depuis Linux 5.13) Quand il est enregistre avec le mode UFFDIO_REGISTER_MODE_MINOR, l'espace utilisateur recevra une notification d'erreur de page lorsqu'une erreur de page mineure survient. C'est-a-dire quand une page de sauvegarde est dans le cache de page, mais les entrees dans la table de pages n'existent pas encore. L'execution du thread fautif sera arretee jusqu'a ce que l'erreur de page soit resolue a partir de l'espace utilisateur par un ioctl UFFDIO_CONTINUE. UFFDIO_REGISTER_MODE_WP (depuis Linux 5.7) Quand il est enregistre avec le mode UFFDIO_REGISTER_MODE_WP, l'espace utilisateur recevra une notification d'erreur de page lors d'une ecriture sur une page protegee en ecriture. L'execution du thread fautif sera arretee jusqu'a ce que l'espace utilisateur supprime la protection de la page en utilisant un ioctl UFFDIO_WRITEPROTECT. Plusieurs modes peuvent etre actives en meme temps pour le meme intervalle de memoire. Depuis Linux 4.14, une notification d'erreur de page d'userfaultfd peut incorporer de facon selective des informations d'identifiant des threads en erreur dans une notification. Il est necessaire d'activer cette fonctionnalite explicitement en utilisant le bit de fonction UFFD_FEATURE_THREAD_ID lors de l'initialisation du contexte d'userfaultfd. Par defaut, la declaration de l'identifiant du thread est desactivee. Utilisation Le mecanisme d'userfaultfd est concu pour permettre a un thread dans un programme multi-thread de realiser la pagination en espace utilisateur pour d'autres threads dans le processus. Lorsqu'un erreur de page survient pour une des regions enregistrees dans l'objet userfaultfd, le thread en erreur est mis en sommeil et un evenement est genere qui peut etre lu au moyen du descripteur de fichier userfaultfd. Le thread de gestion d'erreur lit les evenements a partir de ce descripteur de fichier et les corrige en utilisant les operations decrites dans ioctl_userfaultfd(2). Lors de l'intervention sur les evenements d'erreur de page, le thread de gestion d'erreur peut declencher le reveil d'un thread endormi. Il est possible que les threads en erreur et les threads traitant les erreurs soient executes dans le contexte de processus differents. Dans ce cas, ces threads peuvent appartenir a differents programmes, et le programme qui execute les threads en erreur ne collaborera pas necessairement avec le programme qui gere les erreurs de page. Dans ce mode non cooperatif, le processus qui controle userfaultfd et gere les erreurs de page a besoin d'avoir connaissance des modifications dans la disposition de la memoire virtuelle du processus en erreur pour eviter une corruption de memoire.' Depuis Linux 4.11, userfaultfd peut aussi informer les threads gerant les erreurs des modifications dans la disposition de la memoire virtuelle du processus en erreur. De plus, si le processus en erreur invoque fork(2), les objets userfaultfd associes au parent peuvent etre dupliques dans le processus enfant et le controleur d'userfaultfd sera informe (au moyen de UFFD_EVENT_FORK decrit plus bas) sur le descripteur de fichier associe aux objets userfault crees pour le processus enfant, ce qui permet au controleur d'userfaultfd de realiser la pagination de l'espace utilisateur pour le processus enfant. A la difference des erreurs de page qui doivent etre synchrones et reclament un reveil explicite ou explicite, tous les autres evenements sont envoyes de facon asynchrone et le processus non cooperatif reprend son execution des que le gestionnaire d'userfaultfd execute read(2). Le gestionnaire d'userfaultfd doit soigneusement synchroniser les appels a UFFDIO_COPY avec le traitement des evenements. Le modele asynchrone actuel d'envoi d'evenement est optimal pour des implementations de gestionnaire userfaultfd non cooperatif a thread unique. Depuis Linux 5.7, userfaultfd peut effectuer le suivi synchrone de page sale en utilisant le nouveau mode d'enregistrement de page protegee en ecriture. Il faut verifier le bit de fonction UFFD_FEATURE_PAGEFAULT_FLAG_WP avant d'utiliser cette fonctionnalite. Le mode protection en ecriture, similaire au mode d'origine page manquante d'userfaultfd, genere une notification d'userfaultfd quand la page protegee en ecriture est ecrite. L'utilisateur doit resoudre l'erreur de page en deprotegeant la page fautive et en forcant le thread fautif a continuer. Pour plus d'informations, consultez la section << Mode protection d'ecriture d'userfaultfd >> Fonctionnement d'userfaultfd After the userfaultfd object is created with userfaultfd(), the application must enable it using the UFFDIO_API ioctl(2) operation. This operation allows a two-step handshake between the kernel and user space to determine what API version and features the kernel supports, and then to enable those features user space wants. This operation must be performed before any of the other ioctl(2) operations described below (or those operations fail with the EINVAL error). After a successful UFFDIO_API operation, the application then registers memory address ranges using the UFFDIO_REGISTER ioctl(2) operation. After successful completion of a UFFDIO_REGISTER operation, a page fault occurring in the requested memory range, and satisfying the mode defined at the registration time, will be forwarded by the kernel to the user-space application. The application can then use various (e.g., UFFDIO_COPY, UFFDIO_ZEROPAGE, or UFFDIO_CONTINUE) ioctl(2) operations to resolve the page fault. Depuis Linux 4.4, si l'application definit le bit de la fonction UFFD_FEATURE_SIGBUS en utilisant l'ioctl(2) UFFDIO_API, aucune notification d'erreur d page ne sera transmise a l'espace utilisateur. Un signal est envoye a la place au processus en erreur. Avec cette fonction, userfaultfd peut etre utilise a des fins de robustesse pour capturer simplement tout acces aux zones dans l'intervalle d'adresses enregistre qui n'ont pas de pages allouees sans avoir a ecouter les evenements d'userfaultfd. Aucun controleur d'userfaultfd ne sera requis pour traiter ce type d'acces memoire. Par exemple, cette fonction peut etre utile a des applications qui desirent empecher le noyau d'allouer des pages automatiquement et de remplir des trous dans des fichiers creux quand c'est un mappage memoire qui permet l'acces aux trous. La fonction UFFD_FEATURE_SIGBUS est heritee de facon implicite avec fork(2) si elle est utilisee en combinaison avec UFFD_FEATURE_FORK. Des details sur les differentes operations d'ioctl(2) sont disponibles dans ioctl_userfaultfd(2). Depuis Linux 4.11, les evenements autres que les erreurs de page peuvent etre actives pendant l'operation UFFDIO_API. Jusqu'a Linux 4.11, userfaultfd ne peut etre utilise qu'avec des mappages de memoire privee anonyme. Depuis Linux 4.11, userfaultfd peut aussi etre utilise avec des mappages de memoire hugelbfs et partagee. Mode protection d'ecriture d'userfaultfd (depuis Linux 5.7) Depuis Linux 5.7, userfaultfd prend en charge le mode protection d'ecriture pour la memoire anonyme. L'utilisateur doit d'abord verifier la disponibilite de cette fonctionnalite en utilisant l'ioctl UFFDIO_API sur le bit de fonction UFFD_FEATURE_PAGEFAULT_FLAG_WP avant d'utiliser cette fonctionnalite. Depuis Linux 5.19, le mode protection d'ecriture est aussi pris en charge sur la memoire de type shmem ou hugetlbfs. Il peut etre detecte avec le bit de fonction UFFD_FEATURE_WP_HUGETLBFS_SHMEM. Pour enregistrer avec le mode page protegee en ecriture de userfaultfd, l'utilisateur doit initier l'ioctl UFFDIO_REGISTER avec le mode UFFDIO_REGISTER_MODE_WP defini. Notez qu'il est permis de surveiller le meme intervalle de memoire avec plusieurs modes. Par exemple, un utilisateur peut effectuer UFFDIO_REGISTER avec le mode defini a UFFDIO_REGISTER_MODE_MISSING | UFFDIO_REGISTER_MODE_WP. Quand seul le mode UFFDIO_REGISTER_MODE_WP est enregistre, l'espace utilisateur ne recevra aucune notification quand une page manquante est ecrite. A la place, l'espace utilisateur ne recevra une notification d'erreur de page protegee en ecriture que quand une page existante et protegee en ecriture est ecrite. Apres que l'ioctl UFFDIO_REGISTER s'est termine avec le mode UFFDIO_REGISTER_MODE_WP defini, l'utilisateur peut proteger en ecriture toute memoire dans l'intervalle en utilisant l'ioctl UFFDIO_WRITEPROTECT ou uffdio_writeprotect.mode devrait etre defini a UFFDIO_WRITEPROTECT_MODE_WP. Quand un evenement de protection en ecriture survient, l'espace utilisateur recevra une notification d'erreur de page dont l'uffd_msg.pagefault.flags aura l'attribut UFFD_PAGEFAULT_FLAG_WP defini. Notez : dans la mesure ou seulement les ecritures peuvent declencher ce genre d'erreur, les notifications de protection en ecriture auront toujours le bit UFFD_PAGEFAULT_FLAG_WRITE defini en meme temps que le bit UFFD_PAGEFAULT_FLAG_WP. Pour resoudre une erreur de page de protection d'ecriture, l'utilisateur doit initier un autre ioctl UFFDIO_WRITEPROTECT dont l'uffd_msg.pagefault.flags doit avoir l'attribut UFFDIO_WRITEPROTECT_MODE_WP efface apres la page ou l'intervalle fautif. Mode erreur mineure d'userfaultfd (depuis Linux 5.13) Depuis Linux 5.13, userfaultfd prend en charge le mode erreur mineure. Dans ce mode, les messages d'erreur ne sont pas produits pour des erreurs majeures (ou les pages etaient absentes), mais plutot pour des erreurs mineures ou une page existe dans le cache de page, mais ou les entrees de la table de pages ne sont pas encore presentes. L'utilisateur doit d'abord verifier la disponibilite de cette fonctionnalite en utilisant l'ioctl UFFDIO_API avec les bits de fonction appropries avant d'utiliser cette fonctionnalite : UFFD_FEATURE_MINOR_HUGETLBFS depuis Linux 5.13 ou UFFD_FEATURE_MINOR_SHMEM depuis Linux 5.14. Pour enregistrer avec le mode erreur mineure d'userfaultfd, l'utilisateur doit initier l'ioctl UFFDIO_REGISTER avec le mode UFFD_REGISTER_MODE_MINOR defini. Quand une erreur mineure survient, l'espace utilisateur recevra une notification d'erreur de page dont l'uffd_msg.pagefault.flags aura l'attribut UFFD_PAGEFAULT_FLAG_MINOR defini. Pour resoudre une erreur de page mineure, le gestionnaire doit decider si le contenu de la page existante doit etre modifiee d'abord, ou non. Si c'est le cas, cela doit etre fait a son emplacement au moyen d'un second mappage non enregistre par userfaultfd vers la meme page de sauvegarde (par exemple en mappant deux fois le fichier shmem ou hugetlbfs). Une fois que la page est consideree << a jour >>, l'erreur peut etre resolue en initiant un ioctl UFFDIO_CONTINUE qui installe les entrees de la table de pages et (par defaut) reveille le ou les threads en erreur. Le mode erreur mineure ne prend en charge que la memoire s'appuyant sur hugetlbfs (depuis Linux 5.13) et sur shmem (depuis Linux 5.14). Lire a partir de la structure userfaultfd Chaque read(2) a partir du descripteur de fichier userfaultfd renvoie une ou plusieurs structures uffd_msg, chacune d'elles decrit un evenement d'erreur de page ou un evenement requis pour l'utilisation non cooperative d'userfaultfd : struct uffd_msg { __u8 event; /* Type d'evenement */ ... union { struct { __u64 flags; /* Attributs decrivant l'erreur */ __u64 address; /* Adresse fautive */ union { __u32 ptid; /* ID du thread de l'erreur */ } feat; } pagefault; struct { /* Depuis Linux 4.11 */ __u32 ufd; /* Descripteur de ficher d'userfault du processus enfant */ } fork; struct { /* Depuis Linux 4.11 */ __u64 from; /* Ancienne adresse de la zone remappee */ __u64 to; /* Nouvelle adresse de la zone remappee */ __u64 len; /* Taille originale du mappage */ } remap; struct { /* Depuis Linux 4.11 */ __u64 start; /* Adresse de debut de la zone supprimee */ __u64 end; /* Adresse de fin de la zone supprimee */ } remove; ... } arg; /* Remplissage des champs omis */ } __packed; Si plusieurs evenements sont disponibles et si le tampon fourni est suffisamment grand, read(2) renvoie autant d'evenements qu'il en tient dans le tampon fourni. Si le tampon fourni a read(2) est plus petit que la taille de la structure uffd_msg, read(2) echoue avec l'erreur EINVAL. Les champs definis dans la structure uffd_msg sont les suivants : event Le type d'evenement. Selon le type d'evenement, differents champs de l'union arg representent les details necessaires au traitement de l'evenement. Les evenements qui ne sont pas des erreurs de page ne sont generes que quand la fonctionnalite appropriee est activee durant la connexion de l'API a l'ioctl(2) UFFDIO_API. Les valeurs suivantes peuvent apparaitre dans le champ event : UFFD_EVENT_PAGEFAULT (depuis Linux 4.3) Un evenement d'erreur de page. Les details de l'erreur de page sont disponibles dans le champ pagefault. UFFD_EVENT_FORK (depuis Linux 4.11) Genere lorsque le processus en erreur invoque fork(2) (ou clone(2) sans l'attribut CLONE_VM). Les details de l'evenement sont disponibles dans le champ fork. UFFD_EVENT_REMAP (depuis Linux 4.11) Genere lorsque le processus en erreur invoque mremap(2). Les details de l'evenement sont disponibles dans le champ remap. UFFD_EVENT_REMOVE (depuis Linux 4.11) Genere lorsque le processus en erreur invoque madvise(2) avec les conseils MADV_DONTNEED ou MADV_REMOVE. Les details de l'evenement sont disponibles dans le champ remove. UFFD_EVENT_UNMAP (depuis Linux 4.11) Genere lorsque le processus en erreur supprime le mappage d'un intervalle de memoire soit explicitement avec munmap(2), soit implicitement durant l'execution de mmap(2) ou mremap(2). Les details de l'evenement sont disponibles dans le champ remove. pagefault.address L'adresse qui a declenche l'erreur de page. pagefault.flags Un masque de bits qui decrit l'evenement. Pour UFFD_EVENT_PAGEFAULT, les attributs suivants peuvent apparaitre : UFFD_PAGEFAULT_FLAG_WP Si cet attribut est defini, alors l'erreur etait une erreur de protection en ecriture. UFFD_PAGEFAULT_FLAG_MINOR Si cet attribut est defini, alors l'erreur etait une erreur mineure. UFFD_PAGEFAULT_FLAG_WRITE Si cet attribut est defini, alors l'erreur etait une erreur d'ecriture. Si ni UFFD_PAGEFAULT_FLAG_WP ni UFFD_PAGEFAULT_FLAG_MINOR ne sont definis, l'erreur etait une erreur d'absence. pagefault.feat.pid L'identifiant du thread qui a declenche l'erreur de page. fork.ufd Le descripteur de fichier associe a l'objet userfault cree pour l'enfant cree par fork(2). remap.from L'adresse d'origine de la plage de memoire dont le mappage a ete modifie en utilisant madvise(2). remap.to La nouvelle adresse de la plage de memoire dont le mappage a ete modifie en utilisant madvise(2). remap.len La taille d'origine de la plage de memoire dont le mappage a ete modifie en utilisant madvise(2). remove.start L'adresse de debut de la plage de memoire qui a ete liberee en utilisant madvise(2) ou dont le mappage a ete supprime. remove.end L'adresse terminale de la plage de memoire qui a ete liberee en utilisant madvise(2) ou dont le mappage a ete supprime. read(2) sur un descripteur de fichier userfaultfd peut echouer pour les raisons suivantes : EINVAL L'objet userfaultfd n'a pas encore ete active avec l'operation d'ioctl(2) UFFDIO_API. Si l'attribut O_NONBLOCK est active dans la description de fichier ouvert associee, le descripteur de fichier userfaultfd peut etre surveille avec poll(2), select(2) et epoll(7). Quand les evenements sont disponibles, le descripteur de fichier l'indique comme lisible. Si l'attribut O_NONBLOCK n'est pas active, alors poll(2) indique (toujours) que le fichier comme ayant une condition POLLERR et select(2) indique que le descripteur de fichier est a la fois accessible en lecture et en ecriture. VALEUR RENVOYEE En cas de succes, userfaultfd() renvoie un nouveau descripteur de fichier qui fait reference a l'objet userfaultfd. En cas d'erreur, la fonction renvoie -1 et errno est defini pour indiquer l'erreur. ERREURS EINVAL Une valeur non prise en compte a ete specifiee dans flags. EMFILE La limite par processus du nombre de descripteurs de fichier ouverts a ete atteinte. ENFILE La limite du nombre total de fichiers ouverts pour le systeme entier a ete atteinte. ENOMEM La memoire disponible du noyau n'etait pas suffisante. EPERM (depuis Linux 5.2) L'appelant n'est pas privilegie (il n'a pas la capacite CAP_SYS_PTRACE dans l'espace de noms initial) et /proc/sys/vm/unprivileged_userfaultfd a la valeur 0. STANDARDS Linux. HISTORIQUE Linux 4.3. La prise en charge des zones de memoire hugetlbfs et partagee et des evenements qui ne sont pas des erreurs de page a ete ajoutee dans Linux 4.11 NOTES Le mecanisme d'userfaultfd peut etre utilise comme une alternative aux techniques traditionnelles de pagination de l'espace utilisateur basees sur l'utilisation du signal SIGSEGV et de mmap(2). Il peut aussi etre utilise pour implementer la restauration en mode paresseux (<< lazy restore >>) pour les mecanismes de la fonctionnalite de gel des applications (checkpoint/restore), aussi bien que la migration apres copie pour permettre une execution (presque) ininterrompue lors du transfert de machines virtuelles et de conteneurs Linux d'un hote a un autre. BOGUES Si UFFD_FEATURE_EVENT_FORK est active et si un appel systeme issu de la famille de fork(2) est interrompu par un signal ou echoue, un descripteur perime d'userfaultfd peut etre cree. Dans ce cas, un faux UFFD_EVENT_FORK sera fourni au surveillant d'userfaultfd. EXEMPLES Le programme ci-dessous demontre l'utilisation du mecanisme userfaultfd. Le programme cree deux threads, un qui agit comme gestionnaire d'erreur de page pour le processus, pour les pages dans une region sans demande de page en utilisant mmap(2). Le programme prend un argument en ligne de commande, qui est le nombre de pages qui seront creees dans un mappage dont les erreurs de pages seront gerees au moyen d'userfaultfd. Apres la creation d'un objet userfaultfd, le programme cree alors un mappage prive anonyme de la taille specifiee et enregistre l'intervalle d'adresses de ce mappage en utilisant l'operation d'ioctl(2) UFFDIO_REGISTER. Le programme cree alors un second thread qui executera la tache de gestion des erreurs de page. Le thread principal parcourt les pages du mappage a la recherche des octets des pages successives. Comme il n'y a pas eu encore d'acces aux pages, le premier acces a un octet de chaque page declenchera un evenement d'erreur de page sur le descripteur de fichier userfaultfd. Chaque evenement d'erreur de page est gere par le second thread qui s'installe dans une boucle traitant l'entree du descripteur de fichier userfaultfd. A chaque iteration de la boucle, le second thread appelle poll(2) pour verifier l'etat du descripteur de fichier puis lit un evenement a partir de ce descripteur de fichier. Tout ce type d'evenements doit etre un evenement UFFD_EVENT_PAGEFAULT que le thread traite en copiant un page de donnees dans la region en erreur en utilisant l'operation d'ioctl(2) UFFDIO_COPY. La suite est un exemple de ce qui est observe lors de l'execution du programme : $ ./userfaultfd_demo 3 Address returned by mmap() = 0x7fd30106c000 fault_handler_thread(): poll() returns: nready = 1; POLLIN = 1; POLLERR = 0 UFFD_EVENT_PAGEFAULT event: flags = 0; address = 7fd30106c00f (uffdio_copy.copy returned 4096) Read address 0x7fd30106c00f in main(): A Read address 0x7fd30106c40f in main(): A Read address 0x7fd30106c80f in main(): A Read address 0x7fd30106cc0f in main(): A fault_handler_thread(): poll() returns: nready = 1; POLLIN = 1; POLLERR = 0 UFFD_EVENT_PAGEFAULT event: flags = 0; address = 7fd30106d00f (uffdio_copy.copy returned 4096) Read address 0x7fd30106d00f in main(): B Read address 0x7fd30106d40f in main(): B Read address 0x7fd30106d80f in main(): B Read address 0x7fd30106dc0f in main(): B fault_handler_thread(): poll() returns: nready = 1; POLLIN = 1; POLLERR = 0 UFFD_EVENT_PAGEFAULT event: flags = 0; address = 7fd30106e00f (uffdio_copy.copy returned 4096) Read address 0x7fd30106e00f in main(): C Read address 0x7fd30106e40f in main(): C Read address 0x7fd30106e80f in main(): C Read address 0x7fd30106ec0f in main(): C Source du programme /* userfaultfd_demo.c Licensed under the GNU General Public License version 2 or later. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int page_size; static void * fault_handler_thread(void *arg) { int nready; long uffd; /* userfaultfd file descriptor */ ssize_t nread; struct pollfd pollfd; struct uffdio_copy uffdio_copy; static int fault_cnt = 0; /* Number of faults so far handled */ static char *page = NULL; static struct uffd_msg msg; /* Data read from userfaultfd */ uffd = (long) arg; /* Create a page that will be copied into the faulting region. */ if (page == NULL) { page = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (page == MAP_FAILED) err(EXIT_FAILURE, "mmap"); } /* Loop, handling incoming events on the userfaultfd file descriptor. */ for (;;) { /* See what poll() tells us about the userfaultfd. */ pollfd.fd = uffd; pollfd.events = POLLIN; nready = poll(&pollfd, 1, -1); if (nready == -1) err(EXIT_FAILURE, "poll"); printf("\nfault_handler_thread():\n"); printf(" poll() returns: nready = %d; " "POLLIN = %d; POLLERR = %d\n", nready, (pollfd.revents & POLLIN) != 0, (pollfd.revents & POLLERR) != 0); /* Read an event from the userfaultfd. */ nread = read(uffd, &msg, sizeof(msg)); if (nread == 0) { printf("EOF on userfaultfd!\n"); exit(EXIT_FAILURE); } if (nread == -1) err(EXIT_FAILURE, "read"); /* We expect only one kind of event; verify that assumption. */ if (msg.event != UFFD_EVENT_PAGEFAULT) { fprintf(stderr, "Unexpected event on userfaultfd\n"); exit(EXIT_FAILURE); } /* Display info about the page-fault event. */ printf(" UFFD_EVENT_PAGEFAULT event: "); printf("flags = %"PRIx64"; ", msg.arg.pagefault.flags); printf("address = %"PRIx64"\n", msg.arg.pagefault.address); /* Copy the page pointed to by 'page' into the faulting region. Vary the contents that are copied in, so that it is more obvious that each fault is handled separately. */ memset(page, 'A' + fault_cnt % 20, page_size); fault_cnt++; uffdio_copy.src = (unsigned long) page; /* We need to handle page faults in units of pages(!). So, round faulting address down to page boundary. */ uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address & ~(page_size - 1); uffdio_copy.len = page_size; uffdio_copy.mode = 0; uffdio_copy.copy = 0; if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1) err(EXIT_FAILURE, "ioctl-UFFDIO_COPY"); printf(" (uffdio_copy.copy returned %"PRId64")\n", uffdio_copy.copy); } } int main(int argc, char *argv[]) { int s; char c; char *addr; /* Start of region handled by userfaultfd */ long uffd; /* userfaultfd file descriptor */ size_t len, l; /* Length of region handled by userfaultfd */ pthread_t thr; /* ID of thread that handles page faults */ struct uffdio_api uffdio_api; struct uffdio_register uffdio_register; if (argc != 2) { fprintf(stderr, "Usage: %s num-pages\n", argv[0]); exit(EXIT_FAILURE); } page_size = sysconf(_SC_PAGE_SIZE); len = strtoull(argv[1], NULL, 0) * page_size; /* Create and enable userfaultfd object. */ uffd = syscall(SYS_userfaultfd, O_CLOEXEC | O_NONBLOCK); if (uffd == -1) err(EXIT_FAILURE, "userfaultfd"); /* NOTE: Two-step feature handshake is not needed here, since this example doesn't require any specific features. Programs that *do* should call UFFDIO_API twice: once with `features = 0` to detect features supported by this kernel, and again with the subset of features the program actually wants to enable. */ uffdio_api.api = UFFD_API; uffdio_api.features = 0; if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1) err(EXIT_FAILURE, "ioctl-UFFDIO_API"); /* Create a private anonymous mapping. The memory will be demand-zero paged--that is, not yet allocated. When we actually touch the memory, it will be allocated via the userfaultfd. */ addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (addr == MAP_FAILED) err(EXIT_FAILURE, "mmap"); printf("Address returned by mmap() = %p\n", addr); /* Register the memory range of the mapping we just created for handling by the userfaultfd object. In mode, we request to track missing pages (i.e., pages that have not yet been faulted in). */ uffdio_register.range.start = (unsigned long) addr; uffdio_register.range.len = len; uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1) err(EXIT_FAILURE, "ioctl-UFFDIO_REGISTER"); /* Create a thread that will process the userfaultfd events. */ s = pthread_create(&thr, NULL, fault_handler_thread, (void *) uffd); if (s != 0) { errc(EXIT_FAILURE, s, "pthread_create"); } /* Main thread now touches memory in the mapping, touching locations 1024 bytes apart. This will trigger userfaultfd events for all pages in the region. */ l = 0xf; /* Ensure that faulting address is not on a page boundary, in order to test that we correctly handle that case in fault_handling_thread(). */ while (l < len) { c = addr[l]; printf("Read address %p in %s(): ", addr + l, __func__); printf("%c\n", c); l += 1024; usleep(100000); /* Slow things down a little */ } exit(EXIT_SUCCESS); } VOIR AUSSI fcntl(2), ioctl(2), ioctl_userfaultfd(2), madvise(2), mmap(2) Documentation/admin-guide/mm/userfaultfd.rst dans l'arborescence des sources du noyau Linux TRADUCTION La traduction francaise de cette page de manuel a ete creee par Christophe Blaess , Stephan Rafin , Thierry Vignaud , Francois Micaux, Alain Portal , Jean-Philippe Guerard , Jean-Luc Coulon (f5ibh) , Julien Cristau , Thomas Huriaux , Nicolas Francois , Florentin Duneau , Simon Paillard , Denis Barbier , David Prevot et Jean-Pierre Giraud Cette traduction est une documentation libre ; veuillez vous reporter a la GNU General Public License version 3 concernant les conditions de copie et de distribution. Il n'y a aucune RESPONSABILITE LEGALE. Si vous decouvrez un bogue dans la traduction de cette page de manuel, veuillez envoyer un message a . Pages du manuel de Linux 6.06 12 fevrier 2024 userfaultfd(2)